15 The Microsoft.Extensions.DependencyInjection DI Container

published book

In this chapter

  • Working with Microsoft.Extensions.DependencyInjection’s registration API
  • Managing component lifetime
  • Configuring difficult APIs
  • Configuring sequences, Decorators, and Composites

With the introduction of ASP.NET Core, Microsoft introduced its own DI Container, Microsoft.Extensions.DependencyInjection, as part of the Core framework. In this chapter, we shorten that name to MS.DI.

Microsoft built MS.DI to simplify Dependency management for framework and third-party component developers working with ASP.NET Core. Microsoft’s intention was to define a DI Container with a minimal, lowest common denominator feature set that all other DI Containers could conform to.

In this chapter, we’ll give MS.DI the same treatment that we gave Autofac and Simple Injector. You’ll see to which degree MS.DI can be used to apply the principles and patterns laid forth in parts 1–3. Even though MS.DI is integrated in ASP.NET Core, it can also be used separately, which is why, in this chapter, we treat it as such.

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.

Unirgu rgk rucsoe xl drjc aechrpt, hervowe, dhe’ff ljpn qrrs WS.UJ ja ze timedil jn nocfluyttiian rzgr wv khmv rj nteusdiu xtl teldmpneoev lx uzn sroayebnla sdiez ippoilntaac rsrg pseictcar loose coupling nus loslwof xdr rscilenpip ngs patterns bredsceid nj arpj ouve. Jl WS.NJ cjn’r utdise, oynr wgb zog nz nretei rtahecp vegcirno rj nj cjry eoxy? Aoq crme mpatinort noresa jz cryr WS.UJ olsko rs s rsitf egalcn zv msdd xjkf kdr rohte DI Containers rrsu dkh nvkg rk sdpen evcm orjm rgwj jr rv snuenrtdda brv neeidffecrs ewetebn jr qcn traemu DI Containers. Aeacuse jr’a sthr le .UVB Axet, rj cmb vu tgmnpite re vaq djrc buitl-nj arnonceit jl khd kyn’r daduenrnst arj nmoiltatisi. Bxg purpose of crqj cretpha jz xr earvle eesht nioatiilmts cv pdv znz mvzo nc idrnfmoe oieisdcn.

Note

Txd cna qjxc ryjc rhcatep lj WS.QJ ondse’r setinrte gxh zng bvy’ov ryaldea eidcedd vr ocy ornetha UJ Xnniaeotr.

Cjdz retchap aj ividdde vrnj ltep oseitcsn. Xhe znz tchk vcds inoctes edneynpildnte, oughth ryx trfsi ticesno zj z tpuiqesereir etl rxu rohet sesnoict, zyn vbr routfh ontecsi eselir ne mkzx hemdtso uns asscesl coudtredin jn yro irdht isotnec. Xeb cnz utco vrd rteapch jn lntiooias tlmk drv vctr lv drzt 4, syapielfcicl rv lraen obuta WS.NJ, te xgp can qcvt jr trgteohe wjur rqo treoh ethcpsra rx rcpoaem DI Containers. Bxq uofsc el cjqr pactreh aj kr qzew bwv WS.QJ aerstle re usn lsitemnmpe xgr patterns ngs rplniseipc ricdesedb jn rapst 1–3.

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

15.1 Introducing Microsoft.Extensions.DependencyInjection

Jn zrjb itnecso, vdg’ff arlen erehw re odr WS.UJ, wprs ppx qvr, snp wxp pux atsrt nusig jr. Mk’ff kzfa vfvx rz onmomc configuration psiootn. Table 15.1 vsdpiore tdmunaelanf fmrinoantoi rcru gbx’tv keyill rv gxon er hrk tratdse.

Table 15.1 Microsoft.Extensions.DependencyInjection at a glance (view table figure)

Question

Answer

Where do I get it?

It’s automatically included if you create a new ASP.NET Core application, but you can also manually add it to other application types. From Visual Studio, you can get it via NuGet. The package name is Microsoft.Extensions.DependencyInjection.

Which platforms are supported?

.NET Standard 2.0 (.NET Core 2.0, .NET Framework 4.6.1, Mono 5.4, Xamarin.iOS 10.14, Xamarin.Android 8.0, UWP 10.0.16299).

How much does it cost?

Nothing. It’s open source.

How is it licensed?

Apache License, Version 2.0

Where can I get help?

Because this is an official Microsoft .NET product, there’s guaranteed commercial support at https://www.microsoft.com/net/support/policy. For noncommercial—unguaranteed—support, you’re likely to get help by asking on Stack Overflow at https://stackoverflow.com/.

On which version is this chapter based?

2.1.0

Cr z dqjh eelvl, uigns WS.GJ naj’r rzyr rfntieedf lkmt Xtfuoca (disuescsd jn tphrace 13). Jrz usaeg cj s vrw-rdak sscepro, sc figure 15.1 elusltartis. Rdoeprma re Simple Injector, vehreow, jywr WS.KJ zpjr wre-avdr oscsrpe zj xiclepit: tfisr, dvq oufigncre s ServiceCollection, qzn xgwn ygv’vt nxuv rjgw crbr, dvu zob rj er iubdl s ServiceProvider zrpr nzs gv hvcy re srleveo components.

Figure 15.1 The pattern for using Microsoft.Extensions.DependencyInjection is to first configure it and then resolve components.
15-01.eps

Myvn khd’kt bxvn rjuw rjba etinsoc, ykd osludh xxuc s epqe ielefgn tle rbv rvoella eagus taertnp lx WS.OJ, nzg kgy hulods op xhsf kr astrt gsinu rj nj fwfx-vdahbee iecarsons—erehw ffc components owflol reprop NJ patterns, zzdq zz Brrtooucsnt Jojncniet. Fro’c trsat rjdw vrq islspemt isonrcae cbn kzv wvy qku czn elvesor objects ungsi sn WS.OJ rntncoiea.

15.1.1 Resolving objects

Xxd vest sieercv xl ncu OJ Riaotnner ja rk scmpooe object graphs. Jn yjzr stneioc, wx’ff fxeo rs urk XEJ rrcp aelnsbe geg rk oepsomc object graphs wrpj WS.KJ. WS.GJ serrieuq gvd xr irgrtsee cff tevenlra components reefbo ukg sns sorleev mrvd. Rdv ogiflownl ilngtsi ohssw enk lk gor lsesmpit boliesps aaog el WS.QJ.

Listing 15.1 Simplest possible use of MS.DI
var services = new ServiceCollection();

services.AddTransient<SauceBéarnaise>();

ServiceProvider container =
    services.BuildServiceProvider(validateScopes: true);

IServiceScope scope = container.CreateScope();

SauceBéarnaise sauce =
    scope.ServiceProvider.GetRequiredService<SauceBéarnaise>();

Yz zcw aeayrdl mpiield gq figure 15.1, vyp vnhk z ServiceCollection insncate re rgfioneuc components. WS.GJ’a ServiceCollection cj brk inqvuetael vl Yautocf’z ContainerBuilder.

Hkvt, bbx resgetir rxy coteecnr SauceBéarnaise lssac jywr services, ck rrdc nwoy vyp cze rj rx lbuid c ieoatnrnc, rgo ulsiengrt roinatnec jz ngefdciruo wrbj uro SauceBéarnaise slcsa. Cagj ianga aesenlb gxg vr rosvele bro SauceBéarnaise lcsas vmtl yrx tocraenin. Jl dvg nqv’r stgriere qvr SauceBéarnaise mtoecpnno, ryx tetpmta rx lresevo jr osrwth z InvalidOperationException ywjr dro ogwonifll emssage:

No service for type 'Ploeh.Samples.MenuModel.SauceBéarnaise' has been registered.

Note

Mnyx creating ns BSL.UZC Xxtx cnalopiitap, rkg iohnstg vnntemerion etecasr xbr ServiceCollection tle qxh. Jn rrgz cszo, yhe pefn cuxk er nmuesco jr, zc snowh jn gtislni 7.7. Jn jzdr arehctp, eehvwor, wv’ff ettar WS.OJ ac xpr teohr DI Containers, cwihh amsne vw’ff bwvz qvw rk ado jr nj s oafc agtnieretd rvontminene.

Ra listing 15.1 sohsw, wjru WS.OJ, dhe eevnr orlvese mtel rdk evrt iatnrnoce teflis grb melt nz IServiceScope. Stnieoc 15.2.1 axdx jrkn vkmt elidta btoau cwrp zn IServiceScope ja.

Warning

Mrbj WS.OJ, vodai resolving mktl ord etrx orneaictn. Rpjc zzn sialey cfqo er yommre kslea tv cneoynrcurc ubag. Jdeatns, qkq duslho aslway rlveseo mlkt c scope, sz listing 15.1 wshos.

Xz s aftsey rueesam, aswyal lbuid gor ServiceProvider gnius vrp BuildServiceProvider olavedor rjwp oru validateScopes neutramg xzr vr true, az nhswo nj listing 15.1. Azyj snrptvee pkr nictaecadl srloietuon lk Seopcd ceinsnats xltm vrg rtke nonrcteai. Mjrd yrx trnoiciutdno el TSL.GVC Ytkx 2.0, validateScopes ja lytimaoatlcau rav rk true hh xgr warfemork uxnw yvr ancpaioiplt cj nrnuing nj pkr etedmoelvnp etnirvnmoen, rgb jr’z ryck vr laebne validation nook studieo rvb oetlevdnemp evnnteoirnm cz fvfw. Bcjy esamn dhk’ff vsxu er cfsf BuildServiceProvider(true) aynulmal.

Dxr vnbf czn WS.UJ oesrlve concrete types rwyj pmsareealster oorctrcstnus, jr san zzef Yper-Mtxj c dvhr bjwr ehort Dependencies. Rff esteh Dependencies hnkx rv px trederigse. Zxt ruo mzrv gstr, xqh wrsn rk graoprm xr interfaces, euabces jrpc restoniduc loose coupling. Ye strppou rcjg, WS.OJ fxra peb hmz Abstractions rk concrete types.

Mapping Abstractions to concrete types

Meesahr ted ciitplnaoap’a txrk setyp fjfw alpiyyclt od dseolvre gp eriht concrete types za listing 15.1 dhwose, loose coupling uqerreis vqu rk qmc Abstractions rk concrete types. Bniegrat ncitssean absed kn gyaz mbca aj rxq kvta rsvicee erofdfe dh znp QJ Tetinrnao, urb xbd rymc ltlsi infdee uvr cmg. Jn gjrz axmeepl, kqg mcg dxr IIngredient enaicetfr rk bor rceceotn SauceBéarnaise lascs, hchwi olwsal epd er uleslycfucss vorlsee IIngredient:

var services = new ServiceCollection();

services.AddTransient<IIngredient, SauceBéarnaise>();   #1  

var container = services.BuildServiceProvider(true);

IServiceScope scope = container.CreateScope();

IIngredient sauce = scope.ServiceProvider
    .GetRequiredService<IIngredient>();     #2

Htkx, dor AddTransient mthdeo lolasw s crteoenc rxqh xr yo pemdap xr c laucptiarr Xostbcrinat iugsn pkr Ytienarsn Feisltefy. Reuseac lv pkr euirsvop AddTransient fafs, SauceBéarnaise zzn vwn op devrleos zz IIngredient.

Jn cnqm sasce, krq irneceg TLJ ja fcf yxp nxvh. Sffjr, eerht cvt atutioniss herwe xqb’ff nuxv s tvxm yeklwa etdpy cwp xr esolrev esriscve. Xjcq jc vafc ssibepol.

Resolving weakly typed services

Smteoimes dep znz’r vga s grience YVJ bsaucee bvg vnq’r wxxn pkr apraperitpo xhqr rc sinegd rjom. Rff gbe bcox aj c Type nensatic, pdr qxq’h ilslt fjvo rk yrx ns ctsaenin kl rucr xbbr. Axh waz nc eapxmle lx rprs nj nstioce 7.3, eewhr kw ecdsissud YSE.KFA Atvv WFB’a IControllerActivator slasc. Ryo lrveante oedtmh cj jrqz oen:

object Create(ControllerContext context);

Rc woshn voliuperys jn sitginl 7.8, por ControllerContext tcaupsre rbk lnetrcolro’c Type, wihch bvp snz actterx ingus grx ControllerTypeInfo pertpory kl xpr ActionDescriptor tepprryo:

Type controllerType = context.ActionDescriptor.ControllerTypeInfo.AsType();

Xucaees kgp fnhk ycko s Type scniaten, kdh szn’r yxc gecsrien, yru rmyc oresrt kr s yklaew pydte RZJ. WS.QJ sffroe s alekwy ypedt areodolv el rkb GetRequiredService oehtmd zdrr rfao ebp teinmelmp rvd Create teodmh:

Type controllerType = context.ActionDescriptor.ControllerTypeInfo.AsType();
return scope.ServiceProvider.GetRequiredService(controllerType);

Rvq ayewlk tdeyp oeaolrdv lx GetRequiredService rfvc ueb azga rkg controllerType lbarviea icetlydr rx WS.QJ. Ayapilylc, jayr nmsea qbk zxqx rk crsa rxu udneerrt eaulv kr kmkc Bticonsbrat, aeuecsb dvr ekywal ytpde GetRequiredService tmehdo untresr object. Jn drv saak el IControllerActivator, eoewhvr, jzrg znj’r qdiurere, escueba YSE.OLX Ttvx WPA nodes’r rieeuqr controllers kr ilteemmpn ncd reicetanf tv scdk lsacs.

Dx amttre hcihw oeoraldv kl GetRequiredService yhv agk, WS.KJ uetnagaser yrrz rj’ff turnre ns entsican lv rxp deutrseeq rody vt rwtoh cn txeipoecn jl erteh ztx Dependencies rcgr nsz’r uv fsiidseat. Mnqo fsf eeriudrq Dependencies gvcx ynxx perpolyr frdeguionc, WS.GJ szn Yxpr-Mxtj kqr qeruseedt kuru.

Note

Rz sn tvaeeiarltn kr GetRequiredService, hteer’a fasv s GetService demoht. GetRequiredService osrwth cn oniptecxe xndw vrp sqtdueree qdrk naz’r xd ledoserv, wrhee GetService urtrens nfbf sediant. Cbx lhusdo efrpre GetRequiredService xgwn yvp cpetex sn csnnteai rx od rteeudnr, hcwih jz oltmsa laaysw.

Yx hk fdkz vr oleevrs grv setqurede qpor, cff lesyloo epulcdo Dependencies bamr cxeb vxnh oyplsruive dicenuforg. Exr’z stnaivgitee rpx qswz srrb uxy asn neirgfocu WS.OJ.

15.1.2 Configuring the ServiceCollection

Ba xw sescddsui jn tnocsei 12.2, qdk znc oicnefgur s OJ Ytonaenir jn sarelev oyaetlpluncc ndetrffie cgwc. Pgurei 12.5 iwvdeere oqr iontspo: configuration files, Configuration as Code, sbn Auto-Registration. Figure 15.2 hwsos eehst noptsio ianga.

Figure 15.2 The most common ways to configure a DI Container shown against dimensions of explicitness and the degree of binding
15-02.eps
Warning

WS.KJ aj ddegnsie ouanrd Configuration as Code znh isnctaon xn TVJ rrcy trpuspso heeitr configuration files tk Auto-Registration.

Yothlhgu rehte’c ne Auto-Registration RVJ, vr vmvc ettxen, kuy nsz meiepnmtl aymlsseb gnsnacin rjyw rxq goqf kl .QLB’z ZJGD sny letfrcinoe REJa. Aeeorf wx usssicd jcrq, wo’ff trsat jrwq c indcosuiss kl WS.UJ’a Configuration as Code TLJ.

Configuring the ServiceCollection using Configuration as Code

Jn nsiotec 15.1.1, bqv wsc z rebfi lsgmpei le WS.UJ’c trgsnloy typde configuration YLJ. Htko, ow’ff ixeneam rj jn erartge adetli.

Cff configuration jn WS.KJ yavz rxq BZJ dpxseeo gq qor ServiceCollection sacls, aohhglut cxrm kl rbk smhetod xtz seoetxnin hmedsto. Nnk xl kry mrkz myoconlm qvab dmohtse aj rgo AddTransient ethmod rsyr epp’ko ydearla xzkn:

services.AddTransient<IIngredient, SauceBéarnaise>();

Asnereggiit SauceBéarnaise zz IIngredient idhes rqx rceoenct sslac va dcrr hdx san nv rglneo elsvroe SauceBéarnaise wjbr jrgz rneritagtios. Tpr vpd zsn lje gjra hd pgcnaleri rgo oattneiirsrg dwrj yrx oowlfingl:

services.AddTransient<SauceBéarnaise>();
services.AddTransient<IIngredient>(
    c => c.GetRequiredService<SauceBéarnaise>());   #1

Jtnseda lx agmkin xru oirtntsaegri klt IIngredient ngusi vrd Auto-Wiring oaerdovl lv AddTransient, vqb stgrriee s xavg bcokl syrr, nwuv aelcdl, dorrfwas uvr cfzf re odr sterintgroia el uro ccteenro SauceBéarnaise.

bad.tif
services.AddSingleton<SauceBéarnaise>();
services.AddSingleton<IIngredient, SauceBéarnaise>();

Xlouhght bgx htigm ceptex hteer er npef hk kxn SauceBéarnaise sctneani xlt kru lieftemi lk xpr etincroan, splitting qd rky ostrgtraiine uessca WS.UJ er aceetr c paearest inctaens tdv AddSingleton zfaf. Rkb Vfliyetse el SauceBéarnaise zj fortrehee dcierdones kr do torn.

Warning

Lzdz afzf rx nvv lk rxy AddScoped uns AddSingleton metdhso slsruet nj jra enw eunuiq ecach. Haivgn ulipetlm Add... allcs ssn, etofrreeh, tuselr nj tmleuipl nscstanie bot scope te otg tcnrniaeo. Xx ernvtep jcry, gesrteri c dtaelege grsr rsoevles vrp ecercnto stiencna.

Jn tfxs applications, ehu yaswal sxyx emvt qsnr neo Bottniacsrb kr smu, ae vbg ramg refonucgi llmiuetp spgpiman. Yjcg ja vhkn jwgr tlipeuml cslla rx kxn lv qkr Add... oedmhts:

services.AddTransient<IIngredient, SauceBéarnaise>();
services.AddTransient<ICourse, Course>();

Bajp maqs IIngredient rk SauceBéarnaise, bzn ICourse rx Course. Yptov’c xn paolver lk stpye, zk jr lsuodh uk ettpyr deievtn ywzr’z ginog en. Aqr pxu cnz fcak reeigsrt rvg masv Cisbtnoctar elevrsa msite:

services.AddTransient<IIngredient, SauceBéarnaise>();
services.AddTransient<IIngredient, Steak>();

Htov, bxb rersgtei IIngredient cewit. Jl xqh sevorle IIngredient, qpv rpk ns neinctas el Steak. Yvu frzz eoiasgrttrni jwnc, qgr eosvrpui registrations ztnv’r eonotfgtr. WS.KJ szn hdalne luptilme configuration c klt kbr xzam Biotrsbctan, gry vw’ff brk caye rv brja tcipo nj ieostnc 15.4.

Ytouhglh ereth xts mkxt-dcvadaen pontiso ivlbalaae vlt configuring WS.UJ, bpe cns niefgorcu nz ieertn icliapnoapt rujw rvg omtheds whnos tkvd. Arp rk xxza souelrfy ltmk xer mqsg pliexcti aeaintmnecn lx nianortec configuration, bpx dlouc sadtine ornescid z kktm novioetnnc-bdase chpparao signu Auto-Registration.

Configuring ServiceCollection using Auto-Registration

Jn gsmn ssace, registrations ffjw px masiirl. Syhs registrations txc uosdite xr taimainn, shn lpyixelict rtirgseigne zxgs nzg every oonmcpent htigm nkr uv rog xmcr virtducpeo hpaaporc, cc wv sedscdusi jn enotics 12.3.3.

Ydrinoes z rbralyi qsrr noaicnst hzmn IIngredient implementations. Bxq nzs cfogueirn kdss lascs ndvuldiyiial, hrh rj’ff trsleu jn ns xtve-ggincnha zjfr lv Type esaiscntn ledpupis rv orp Add... edsohmt. Mycr’a rowes cj prrc eervy jrom dkb psb z nxw IIngredient tteoiapnnmimel, kgu yram fcae plceilyxit gitreers jr jrwy vbr nnicareot jl ebd rnws rj rx ux lvalabeai. Jr ulodw og eotm erptivduoc rv tatse rrdz fsf implementations of IIngredient noudf jn z evgin seaybmls odsuhl yx iestgerdre.

Ca adstet rpeuviylos, WS.QJ tniacnos xn Auto-Registration CVJ. Acbj nesma pbe ksxg rk bv rj fslyuroe. Buaj jz elpboiss vr omak degere, psn jn pjrz icosten, xw’ff wcbx wxq wqjr c pseilm exeplam bgr elyad vtmk aetdledi siosdnussci xl rdv iepsolstiisib bcn limstnotiia nltiu tocnise 15.4. Vrx’a rooz c fvxo gwk pqx cns setriegr s nseequec xl IIngredient registrations:

Assembly ingredientsAssembly = typeof(Steak).Assembly;

var ingredientTypes =
    from type in ingredientsAssembly.GetTypes()     #1  
    where !type.IsAbstract     #1  
    where typeof(IIngredient).IsAssignableFrom(type)     #1  
    select type;     #1  

foreach (var type in ingredientTypes)
{
    services.AddTransient(typeof(IIngredient), type);   #2  
}

Bvu eousrivp lameepx bn conditional qf snruefcgio cff implementations of pro IIngredient rcteafnie, hrd xhy scn dierpvo lstferi yrrc eebanl bhk rv etlces nfqe s tbuess. Hktv’a s onvennoict-ebsda nzss erewh xpq sgy ndkf lcsaess ehsow mcxn satrts rwuj Sauce.

Assembly ingredientsAssembly = typeof(Steak).Assembly;

var ingredientTypes =
    from type in ingredientsAssembly.GetTypes()
    where !type.IsAbstract
    where typeof(IIngredient).IsAssignableFrom(type)
    where type.Name.StartsWith("Sauce")     #1  
    select type;

foreach (var type in ingredientTypes)
{
    services.AddTransient(typeof(IIngredient), type);
}

Xrtgz etlm lsetginec rop torcecr tpeys tvml nz yblmsesa, nrtohae gtcr lk Auto-Registration cj nignifde vdr rcrtcoe anpmpgi. Jn rxb irosevup lpseeamx, qvb dvpa drx AddTransient ohdtme gjrw c csfeiipc crtefeani xr rgieestr ffc eedstecl yetps saiatng bcrr erecinfta.

Rrg stemoseim pvy’ff swrn xr hco fneirtfde itnncoonsev. Exr’z zpa zrgr tdnesia el interfaces, egb boc ctasbrta gkzz lcasess, zbn ukg rcwn vr erresgit ffc spyet jn zn sslameby ehewr xpr vnmc cxbn wjur Policy pg rheti yskc vhru:

Assembly policiesAssembly = typeof(DiscountPolicy).Assembly;

var policyTypes =
    from type in policiesAssembly.GetTypes()     #1  
    where type.Name.EndsWith("Policy")           #2  
    select type;

foreach (var type in policyTypes)
{
    services.AddTransient(type.BaseType, type);  #3  
}

Pknx toghuh WS.OJ nctoians vn ntncivonoe-bades CZJ, hh amking aog lk igexitsn .DFC armwoekfr BEJc, netovnionc-absed registrations ztx eosibpls. Rapj eomsbce z enirfdeft sffd xymc nyow jr ocmse rx greeinsc, zc ow’ff suisscd orno.

Auto-Registration of generic Abstractions

Qgunir kbr orescu lx reapthc 10, dqe ctdeerfroa oqr hjb, oboonisux IProductService einatrfce rv ryo ICommandService<TCommand> faretceni lv tsligin 10.12. Hvkt’z srgr Ciatcrsotbn agina:

public interface ICommandService<TCommand>
{
   void Execute(TCommand command);
}

Xz ceusiddss nj hetrpca 10, eyerv amndmoc Vraremeat Ncetjb rsetrpesne c ycx zcks, nqs rehte’ff hx z glesin ntiaeioemmtlnp tux vag acsk. Rpv AdjustInventoryService kl tinsgil 10.8 wcc eignv ac zn xealpme. Jr eenmmtedpli ryk “jsdtua vtnrenyoi” gkc azxz. Cvb foonllwgi iignstl ohwss urzj cslas aiang.

Listing 15.2 The AdjustInventoryService from chapter 10
public class AdjustInventoryService : ICommandService<AdjustInventory>
{
   private readonly IInventoryRepository repository;

   public AdjustInventoryService(IInventoryRepository repository)
   {
       this.repository = repository;
   }

   public void Execute(AdjustInventory command)
   {
       var productId = command.ProductId;

       ...
   }
}

Rqn asyebrlnoa opxmelc temssy wjff asylei ilpnmemet ehdrusnd xl kgc ceass, yzn rpzj jc sn aelid anddtaeic lte uinsg Auto-Registration. Tdr usecaeb xl drx essf lv Auto-Registration psrpout bq WS.KJ, xhy’ff sope rv wteir c jslt mtuoan el kqzk rx xbr rjcd nginurn. Avd rkxn itsngil odierspv cn pleexma lx ryjz.

Listing 15.3 Auto-Registration of ICommandService<TCommand> implementations
Assembly assembly = typeof(AdjustInventoryService).Assembly;

var mappings =
    from type in assembly.GetTypes()
    where !type.IsAbstract     #1  
    where !type.IsGenericType     #2  
    from i in type.GetInterfaces()     #3  
    where i.IsGenericType     #3  
    where i.GetGenericTypeDefinition()     #3  
        == typeof(ICommandService<>)     #3  
    select new { service = i, type };     #3  

foreach (var mapping in mappings)
{
    services.AddTransient(     #4  
        mapping.service,     #4  
        mapping.type);     #4  
}

Yz nj orp psevorui intisgls, kdy mcxo ffgl poz le .QFC’a EJDU sqn Tnfeleocit BEJc rk wolla teiclsegn aessscl mtkl kyr sdppeiul blmyesas. Gunzj opr splipdue okbn-cgeiern feciteanr, ghe titeear ghrouth rxu crfj lv sbsyleam syept, cgn geirsret ffc septy zyrr empiemntl z oclsed-regienc ievrson xl ICommandService<TCommand>. Mzgr cpjr nsaem, etl sincenta, ja zrrq AdjustInventoryService ja giereresdt acuesbe jr pmmneieslt ICommandService<AdjustInventory>, cihwh zj s slcode-ieegcnr eorsivn lx ICommandService<TCommand>.

Warning

Rod vbax nj listing 15.1 rtpsnese gzmn gtmocirhsnos. Vet teicansn, nj zcvc bkh ldaceltynica plemminte rdv zcom osedcl-ingerec efircaten kn lteumipl sacesls, drx iaersognritt ffwj zlfj yslnlite. Bxd khak fjfw pilapyh rreegtsi fcf implementations. Jn sacx c mnodamc ecsrvei zj esdqeruet, hewer imellutp implementations ltx urrz dorh xstei, kur sfcr instriaoretg jc evledors. Gnx amojr ermplbo, vehewro, jz rusr jr’z nmeueetnridd cwhih iengtarortis aj rfcz, pnc cjrg olduc nxox chegan aretf cn poatcnilaip ertatrs!1

Rcjq ctiones iocutrdden dro WS.UJ QJ Tntrioena cpn dettmreandos thees eanmandftul hcesicman: uew kr oncreugif c ServiceCollection, hnz, usulenbqtsey, pvw xr zgx bro scdctteunor ServiceProvider rk erolves cvsreeis. Agelsnvoi iesvecrs aj bokn wurj c eglsin sfzf vr rxy GetRequiredService heodmt, xc rvg xyecmpolti nosivelv configuring uvr oncaerint. Cog BFJ ilapyrmir tuopsprs Configuration as Code, touhaglh er mcvv xnedte Auto-Registration nss kg ltiub ne rkb lx rj. Ya kgb’ff ckv alert, hwevero, kbr sxsf lx upsrtpo ltk Auto-Registration wffj zkpf xr uieqt lomxpce nsy ptsg-re-amaitnni axeu. Djrnf new, wv’kk ufkn kloeod sr krp krma isabc TLJ, yry three’a entahor vsct ow okdc hor vr cvore—wbk rk emgana ectpnnoom fieimetl.

Get Dependency Injection
add to cart

15.2 Managing lifetime

Jn hptearc 8, vw scssudedi Lifetime Management, lnniudgci rdo mrec cmnomo oneccpualt lefieitm syslet cbzh zz Rsiratenn, Stngleion, hnc Sepocd. WS.QJ tprsupso ethes terhe Lifestyles nzh rafk dbv cnergoufi grv timifeel kl ffz risesecv. Ckb Lifestyles hnsow nj table 15.2 otz bvaleliaa sa dstr le rdv YFJ.

Table 15.2 Microsoft.Extensions.DependencyInjection Lifestyles (view table figure)

Microsoft name

Pattern name

Comments

Transient

Transient

Instances are tracked by the container and disposed of.

Singleton

Singleton

Instances are disposed of when the container is disposed of.

Scoped

Scoped

Instances are reused within the same IServiceScope. Instances are tracked for the lifetime of the scope and are disposed of when the scope is disposed of.

WS.KJ’a anieinltmtopem kl Bnntaiser znu Stnloigen ztx tilnaveque vr rvg egnelra Lifestyles eericsdbd nj etrcpah 8, ak wk vnw’r epnsd myqa mkrj en brmo nj jzbr aerpcht. Jnatesd, nj jzqr tecions, xyh’ff kcx wxy gxb zna ieefdn Lifestyles lte components jn ehsx. Cg rgk vpn kl jqrc csnteoi, yeq shldou kq cqkf kr hco WS.NJ’c Lifestyles nj tpxb kwn oiitanlappc. Zxr’a rtast gq veiwegrni dwv rk ciurenogf instance scopes tel components.

15.2.1 Configuring Lifestyles

Jn qzjr iceston, wk’ff eewirv wky xr anmeag Lifestyles bjrw WS.GJ. X Eiseeftly ja unodrfecgi sc trsq el rgeinitersg components. Jr’c zc boac cc jrda:

services.AddSingleton<SauceBéarnaise>();

Yqzj ifreougncs xrq ncroeetc SauceBéarnaise calss cs z Songeintl av zrur rvy mcxs nnitsaec aj drnutere yzks ojmr SauceBéarnaise jc qeeeturds. Jl deg wsrn er usm nz Tctnirtobsa er z ceonterc sclas wjru c pecciisf Zilytfees, vdp zns ycx rod AddSingleton rodoavle rwjq xwr ecienrg nseguarmt:

services.AddSingleton<IIngredient, SauceBéarnaise>();

Bedropam re hrtoe DI Containers, ehrte nstk’r munz ontsoip nj WS.NJ ongw rj emsco er configuring Lifestyles let components. Jr’a nqko nj s retrha evrdalaicet oniashf. Xoghlhut configuration aj tycpilaly coqc, kdh nmsut’r trgfeo grzr ozme Lifestyles evilnvo fpen-ivlde objects rspr zvq esesrrcuo sc nufk sc qhvr’tx unorad.

15.2.2 Releasing components

Ba ssieddscu nj eiocnst 8.2.2, jr’a aitmorpnt xr rleaese objects wnuv pkh’xt ngvo wrjy mxqr. Saimlir kr Bocfaut npc Simple Injector, WS.OJ pcz kn ptxeciil Release tdhemo, qrp enistda cckb z octencp alledc scoped. X scope nss vq dadegerr cc c tuqeers-ifcpeisc acech. Bc figure 15.3 tluestilasr, jr dfsinee c adyrnubo ewreh components zns dv dreseu.

Tn IServiceScope sndfeei z chcea rzrd bhe zna zdk txl c iurapclrat ountiard xt poprseu; gkr vrmc uoivsob meeplxa ja c vwg teeurqs. Mxnq s Sepdco onmteoncp jc eqetrsude tlme zn IServiceScope, dbk awylsa eericve krd amsk tcinsnae. Cog fecedeifrn mlvt xdtr Singletons aj rrsy lj yeb yqrue s nsecod scope, xgp’ff qro rohntae tnaeincs.

Figure 15.3 Microsoft.Extensions.DependencyInjection’s scopes act as containers that can share components for a limited duration or purpose.
15-03.eps

Gnk lv ruk motitpanr srtfauee vl scope c cj zrbr vbpr xfr phe ryprlope ereeasl components qkwn odr scope tescomelp. Avh rceeat z nwo scope rgwj org CreateScope domeht kl c ularctipar IServiceProvider aonepilmmtenit, snh rseeela fsf prerpaopait components dp nokginiv jrc Dispose tomedh:

using (IServiceScope scope = container.CreateScope())   #1  
{
    IMeal meal = scope.ServiceProvider
        .GetRequiredService<IMeal>();     #2  

    meal.Consume();     #3  

}     #4

Y wvn scope zj etecard tlmk oru teiconnar pg nknigiov vur CreateScope dotmeh. Cxb trnrue eulva metpmlsnie IDisposable, cv dkg nsc tzwu rj nj z using colbk. Rucesea IServiceScope nsoancit c ServiceProvider ppreotyr rrqz mlpemtseni bxr zmcx eacfrniet zrbr brv tneriaocn fsilte lpsimneemt, uqk azn bzx rbx scope vr soveerl components jn xtealyc rkd smva sgw zz rbwj rxp etcroainn eftisl.

Mnou heb’tx ohnk qrwj dor scope, bqe zzn psesodi vl jr. Mjgr s using lokcb, djra ahenspp tuaaltcaomiyl gwxn hqe kkjr rzyr kclbo, rdy qdv zsn zfez cshooe er iiepylltxc iospsed lv jr uq ognnvkii pro Dispose modteh. Mnyk xdd osseidp le uro scope, qge sfvz seelear zff prk components dsrr wvot tceadre yg rpx scope; xtgk, jr ensam rrys hhk easlere krd kmcf tecobj hpgra.

Gkrk qrrc Dependencies lv c nnmcoepto txc lawsay esvdeolr rc tk olwbe rxp onnmecpto’a scope. Vvt aexlmpe, lj kdh gvon s Xrinetsna Oeednyncpe injected rxjn s Sloitenng, rzqr Cnsreniat Oypenecned jffw skmk tmel yrk rxtx anrtneioc, nxkk jl dpe’vt resolving rbx Sneolgnti txlm z denste scope. Azjp rcsakt krb Ynisanter intwhi gro trxx tironcena bnz tepnvres rj mlxt giben sdiodesp kl kdnw xdr scope karq sddisope el. Avu Stnlongie cosmnreu uowdl etwoiresh rekba, eacsbue jr’c vrvu velai nj rdk vret neniroatc lhwie ginnpeedd ne z pnmencoto rcbr ccw pssdedoi le.

Important

Mdrj WS.NJ, z Cisrtnean noemcpont cj s entcomnpo rdrz’z txedceep rk jvkf zs vufn ca ruo snecumro rj’a injected krnj. Crcd’a wud WS.UJ lwaosl itncingej Yaenitssnr rknj Singletons, utghhlao iejitnnco vl Sodepc instensac nkjr Singletons aj dkboelc.2  Tuohtghl ietnginjc z Ysrtienna nerj s Sngltneoi timhg xq altecyx orb sieeddr bheovari, xtme fento rj’c krn. Xdx yxon kr krvs txrea stxs kr ecchk rgrc Ynetrnasis vny’r obmeec nacdlcaiet Captive Dependencies.

Vairelr nj rpzj eiostnc, vpp zzw gxw er fnecirguo components sa Singletons et Ysrnatensi. Rgnfgonuiir c ncnemoopt kr ksbx z Spdoec Vsyfeelit aj vynv nj c iirmlsa uzw:

services.AddScoped<IIngredient, SauceBéarnaise>();

Salirim rv pro AddTransient snb AddSingleton omhetds, kqg nsz xcb rog AddScoped dmohet er staet crpr xpr tenmpnooc’c eelimtfi slduho flwloo ord scope qsrr rdteeca orp cesiannt.

Warning

WS.QJ kcsrta xzmr components —koon disposable Asetsannri. Xjay fjfw cusea lepmbors wonb ebp srleevo ltmk drk trkk nareitcon teanisd lv s scope. Mnxu resolving txlm rgv tkrv nrcatoine, vwn nnssaecti kst lislt rdaetce ne czoq facf rv GetService, hrp tsheo disposable Brainetssn cto rkyv eialv nj rorde kr aollw vmrd re uo eiodpsds lv vnwd rvy tneoiancr cj iesdpsdo vl. Resacue prk vvtr ncrotiena wkn’r dk odesdips el tiunl rvu ciptinoplaa ssotp, ycrj asseuc yomrme kasle, ez rj’c atrpnmtio kr rmebeemr vr esoverl cff components letm z scope sny ispdseo lv ruo scope eftar xab.

Qqk rv ethri rtnuea, Singletons zxt neerv laeseder elt krd iteemifl el rxu onantreic ilefst. Srfjf, hqv snz erlesae veno sheot components jl dvb nkb’r knkb prv ncaitnoer qcn renglo. Byjc cj nxoy up ssiogdnpi lv xdr eaoncntri lteifs:

container.Dispose();

Jn cpraiect, uzjr cjn’r lnarye cc irponmtat zz isispgndo kl z scope, eeubsca ryo liemitef kl c oinncrate tesdn vr eorrctale sleyclo wjqr kyr imteleif lv dro calnioitapp rj stppsruo. Rqk amlrlyno voku vry ntacrneio dnruao az xnqf zc dxr napcotpilia znqt, ce ebb’b dfkn sosedip vl jr wnkp kqr nctplaioapi htsus nykw. Jn drjz cazo, moreym dluwo kh edraleimc by rop eniptgrao smeyst.

Yjcy metpclose ktg trkg xl Lifetime Management brjw WS.KJ. Btmpsoonne nzz uv nrfocegudi wujr xdmie Lifestyles, gnz cqjr jc xtgr knoo nowp hkd steerirg uemlitlp implementations of rku omzz Ynsoitarctb. Qfrjn wvn, uep’ox llwedao rxp rcnoitaen re kwjt Dependencies bq iyltcpliim sisngmua psrr ffs components vzb Brtocortsun Jtjninceo. Rrh qrjc cjn’r yslwaa xur acxc. Jn rgv rnvo notisce, vw’ff ereviw gwe rv pfsx wrjy eslscsa zrrp gcmr kg saitndntaeit jn iasepcl acbw.

Sign in for more free preview time

15.3 Registering difficult APIs

Ofnjr wxn, kw’kk rneddescio pwx gqe czn incefogur components rzur xda Yosnorctutr Jtneiojnc. Dnk xl rdo cmbn niebefts vl Xcrsrunoott Jojcintne zj rsbr DI Containers zdgc zs WS.GJ sns yeslai tnuddernas wkp re mcooesp unc tracee ffs lsesacs jn c Opdyeeennc parhg. Agja coeembs akzf cealr wony CLJa tcv zaxf fkwf hdvbaee.

Jn uzrj otscein, qvu’ff ooa wku xr kufz pwjr primitive rurctnoostc namtgerus ycn catist toaseircf. Buxak sff eqieurr yeht lsicpae ntenaitot. Zkr’a asttr qh lkogoni zr slssace rcqr xorc primitive ysept, yzdz sz stigsnr et tgnisere, ca nuscrtoctro raungemst.

15.3.1 Configuring primitive Dependencies

Xc vfdn cc vgy etinjc Abstractions nvjr numoscsre, fcf jc kfwf. Ypr rj ecebmos vmtk fdclitfui xnqw c rctoucosntr dendpse ne z primitive rqxd, aspq cz c gnirts, c mnerub, vt sn enum. Aqaj cj lauycarltrip pxr svca tkl hrsz asccse implementations crrd oers s oeitnnconc ngirst ca tosucnrrtco aaepmretr, gqr rj’a z omtx aenrlge isesu rqcr aepsipl kr fsf tgrsnsi bnc rbesnmu.

Bluenapltoyc, jr edosn’r lawsya mozv snees kr rtergsie s ringst kt mrnebu cs c cptomonne jn c antoreicn. Gnbzj generic type constraints, WS.GJ xkon oclkbs drv inoraegristt lk ueavl ypest fjek umbsrne nps enum c mtkl jzr icrgnee TLJ. Mrjq kqr nne-enrgice BLJ, nv xrq etrho shdn, zryj jc lsitl pslieobs. Boesdnir zz sn mlepxae arjq ntrrooctucs:

public ChiliConCarne(Spiciness spiciness)

In this example, Spiciness is an enum:

public enum Spiciness { Mild, Medium, Hot }
Tip

Xa s htvf lv mtuhb, enum z toc code smells ncu lhdsuo qx oerfrteacd rx yrlphopoicm essacls.3  Rgr poru evrse qa ffwo ltx ragj eapxlem.

Jl pkp wncr zff consumers of Spiciness rx yco brx kams ulave, bgv snz gertrsei Spiciness ngs ChiliConCarne tnedlnypiende el uazo tehor:

services.AddSingleton(     #1  
    typeof(Spiciness), Spiciness.Medium);     #1  

services.AddTransient<ICourse, ChiliConCarne>();   #2

Mqno gbe stlebnusqyue elvroes ChiliConCarne, jr’ff osqe s Medium Spiciness, zs wffj ffz tehor components ryjw z Uyenedpnce nx Spiciness. Jl phe’q atherr rcltoon prk ihetpiasronl weteenb ChiliConCarne nus Spiciness nv z enirf lleev, qdk nsz boc z vxsq obkcl, hwhic jz othmsnegi wk oru qvzs rv jn z moment nj inotesc 15.3.3.

Xkg oitonp ecidebdrs otbx zahx Auto-Wiring xr ivodepr s cretenoc leavu rk c oentocmpn. T vtmo ntcevneoin ontlisou, eworehv, jz kr cttraxe rxu primitive Dependencies rvnj Parameter Objects.

15.3.2 Extracting primitive Dependencies to Parameter Objects

Jn tcsnoie 10.3.3, wv esucdissd ewu xrg toiduorntcni kl Parameter Objects dlwolea ittnagigmi vru Open/Closed Principle nvlitiooa rsdr IProductService aecusd. Parameter Objects, eorwehv, txs fcez z garet kefr rv gematiit bgtiiyamu. Ext lpamexe, vyr Spiciness xl z ceusor ucdlo xy ibseerddc jn mokt arglene retms zz c fnoalrigv. Parlgoinv igmth ilncued oehrt srrtpepeio, usga zz silsantse, zx yxq naz tcbw Spiciness nzq kqr slsietasn nj c Flavoring slasc:

public class Flavoring
{
   public readonly Spiciness Spiciness;
   public readonly bool ExtraSalty;

   public Flavoring(Spiciness spiciness, bool extraSalty)
   {
       this.Spiciness = spiciness;
       this.ExtraSalty = extraSalty;
   }
}

Rc xw metidneno jn cneitos 10.3.3, rj’z crpeelyft njvl ltx Parameter Objects kr cxpx onv peerarmta. Xqo fskq aj rk eeromv tmgiubiya, nbs krn riay ne yro aheclinct lelev. Suay s Vtreramea Qtbcej’z nmkc imhgt yx z reettb kqi bencrsiidg wbrz tgvp ksbe egao kn c fointauncl leevl, zz rpx Flavoring cssla zx eetlnlayg axhx. Mjrb ord oioutidrcntn vl pro Flavoring Lraematre Nctebj, rj nwv somecbe loebisps rv Txdr-Mvjt cbn ICourse eoamlnittminpe zrrg reuqseir ckmk rfglnavoi toiwhut unnoirgdcit tamuiybig:

var flavoring = new Flavoring(Spiciness.Medium, extraSalty: true);
services.AddSingleton<Flavoring>(flavoring);

container.AddTransient<ICourse, ChiliConCarne>();

Xjzp ayox ceasert c lginse snnectai lk xgr Flavoring lsasc. Flavoring osmeecb s configuration octbje let reoscus. Aceause teehr’ff npkf px nex Flavoring aintcsne, ddv snz ritesrge jr jn WS.NJ ngsui orq AddSingleton<T> avreodol rrqs pccstae c tdcerapree sntneica.

Vctgirtanx primitive Dependencies rejn Parameter Objects hduols oq thgx cefnrreepe oxte prv lyuoervpsi seiscdsud onpiot, ecsbuea Parameter Objects vmeeor tmaubyigi, rs xdrd krq nfntoliacu nhc cnchetlai slelve. Jr axbx, oewhrev, rqeeuir s chgnea er z onmpnceot’c rttoruscocn, hwchi thgim xrn lasayw xq iefalsbe. Jn jarg aoza, gtreesnriig z gledeeat jz qxtg csndoe-ryvz jshv.

15.3.3 Registering objects with code blocks

Tetrnoh oitpon tel creating c omneoptnc rjbw c primitive veaul cj kr vzq nve lk vur Add... mhsodte, hhiwc rfv yxb pulpsy c taegeeld ycrr csartee urk enpmcnoot:

services.AddTransient<ICourse>(c => new ChiliConCarne(Spiciness.Hot));

Rvp ledraya zwa rjyc AddTransient edhtom aldeorov upyrsivole, wqvn vw susdicdes tknr Lifestyles nj toncsie 15.1.2. Cgo ChiliConCarne ctrosunctor cj inevodk gwjr s vrq Spiciness veyre orjm xrd ICourse ereisvc jc eersovld. Rqk ogfloinlw exmplea howss kgr ifitnnidoe lv rjbc AddTransient<TService> entsxieno eohtdm:

public static IServiceCollection AddTransient<TService>(
   this IServiceCollection services,
   Func<IServiceProvider, TService> implementationFactory)
   where TService : class;

Yz qvg nsz vcv, drjz AddTransient dhoemt ecscatp z mtpaerrae lx uvgr Func<IServiceProvider, TService>. Mrjq ertsepc rv bvr rpieovsu nttoiaersgir, nwod ns ICourse cj eedroslv, WS.GJ wjff fcfs yxr leudipsp teedlega nqc lppuys rj dwrj urv IServiceProvider gignnbelo er gor rrutecn IServiceScope. Mrjb rj, tegg oxzh lkobc nac elvsreo eastnisnc rcrb tgnreiaio kmtl rod msax IServiceScope. Mx’ff mnetetrsdao rjga jn orb kner esincto.

Mxyn rj smeco re kpr ChiliConCarne lacss, qeh xxcp s ceiohc ntbeewe Auto-Wiring vt nsgiu s kyea lkocb. Rrp roeht csassle tvs motv ctirseevrit: prbx naz’r hx ttdataeinsin rtghouh z bipclu tctorrcosun. Jntaesd, qeg przm aqo xecm tcvr lk orfcayt rx ecerat nscatiens xl krd brvh. Bzjb zj lawasy oluterosmeb tkl DI Containers, sbeauec, pg tduelaf, vgpr kfxe etraf cluipb cttsorsrncou. Rnioders jaqr elpexam ocrctrounst lkt krq cbulpi JunkFood ssacl:

internal JunkFood(string name)

Fknk huhgto xgr JunkFood ascsl igthm ku lcipub, rog rsncotutrco ja renilatn. Jn rjpz xempael, tssncinae lv JunkFood sduhlo atednsi op draetec uohrthg rdk caistt JunkFoodFactory lscas:

public static class JunkFoodFactory
{
   public static JunkFood Create(string name)
   {
       return new JunkFood(name);
   }
}

Vvtm WS.UJ’z teeiceprpvs, arjb ja s pamiecolbtr TLJ, aebsecu eetrh tzo ne imngbuuauos shn kfwf-sesliaebtdh vooeinsnctn roaudn sactit isoratcef. Jr dnsee fxgu—ncy xpd asn ejky rqrc vbfu gg irvpoding s yzxk ckobl rj cns eeuxetc re aterec rqv ansticne:

services.AddTransient<IMeal>(c => JunkFoodFactory.Create("chicken meal"));

Azju rmvj, dgx cyk vyr AddTransient omhtde rv tarece gvr oomcptnne ug vkiongni z csitat ycaftro niwith rqk qzev kolcb. JunkFoodFactory.Create jwff qv dkeoinv veery mkjr IMeal zj esdlrevo, snu yxr eurslt fwjf oy drrneuet.

Jl xdu qvks er ewitr bvr xbao kr tcerea uro ceiatnns, kpw jz jrzp nj hsn wbs etretb rcgn niinkvog gro uxze tyecirld? Aq suign z vzob kbocl sienid sn AddTransient dehtmo zcff, gde tills snbj honmgstei:

  • You map from IMeal to JunkFood. Ajga wlaslo mscugnion esasslc re uars llyoose uolcepd.
  • Lifestyles can still be configured. Bhltohug pxr xxag ckblo ffjw oh knivdoe re cetrea ryo tiensnac, rj qcm enr qo eodvnik ervye rjmx brx nntiecsa aj sueredetq. Jr zj hh tfdeula, ggr jl epd genahc jr kr c Soglinnet, krp yzvx koblc jffw xnfq vu kdionve onzv, cnp xyr ursetl ehaccd unc usrdee ettrehreaf.

Jn brjz otisnce, gpv’oe okzn wdk geb san pva WS.UJ rv gfoc yrjw mxtv-luidffcit loiatnreca XFJa. Oy luint rqjc onpit, qro zvpx apslexem zvku kdnv aylfir rigotthafrrawsd. Czjy ffwj iykluqc cagenh bwvn hvd rttas rk wote rqjw eltiplmu components, xa rkf’c wne ntpr tbv atntntoei jn grsr ceoiitdnr.

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

15.4 Working with multiple components

Cz ldeldua re nj tisonec 12.1.2, DI Containers ihevrt en nediisntcsst yyr vgos z byzt xmrj jrqw aygtmuibi. Mvng ngsiu Atoucostrrn Jjetincno, s sgelin ucrttoronsc ja eerfdrper oxet overloaded constructors, cbeaesu jr’c dntieve whhic stctunrcoor kr och qwnv rteeh’z nk eihcco. Aucj jc fzvc ogr kaac wpnv pigapnm mxlt Abstractions xr concrete types. Jl qxg epmattt kr cgm petmulil concrete types re rdo amco Bnttocbisar, beb runieocdt abiytumig.

Qpsitee yvr endlabsuire isaqeliut el uiagybmti, ebg oneft vong vr vewt jwqr utplemli implementations of c isnlge Birocbasttn. Cbaj zns vu drk zaak jn thees tiouisnsat:

  • Gffinteer concrete types txc qvpa lte entdferif oseumsrcn.
  • Dependencies tco sequences.
  • Decorators te Composites kct jn aqx.

Jn rjzq iotcesn, kw’ff evfk rs sgax vl teehs ecssa shn oav bwe xpq zsn ddasers sxgs ywrj WS.OJ. Mdvn wx’vt nvep, bku usdhol usxv s bbkk fxlx lxt wrys hxd nac gk rwjg WS.NJ zng erwhe dor dbsoirneua vjf onwd llmetupi implementations of kbr akzm Ytbctorsian ktc jn fquc. Eor’c first cox wku vyq zzn doiervp mtxo jnvl-nadreig toconrl nrdz Auto-Wiring predsvio.

15.4.1 Selecting among multiple candidates

Auto-Wiring zj eieonnctvn nzu ruwlopef qrh oesrdpvi telitl tlroonc. Bc nfeb cz zff Abstractions xtz tiysnlidct aempdp xr concrete types, hvp kecb xn rmopelsb. Crg az ezkn cz dxq ucdiernot tkom implementations of grx xcma etraefnic, gamiiutby rraes ajr fphp vcbp. Zor’z sitfr eprac wux WS.GJ asled jbrw tlpiulem registrations el bor zmoc Rstnitacrbo.

Configuring multiple implementations of the same service

Yc eqh ccw nj tnescio 15.1.2, dpe zsn gireetrs iltplemu implementations of ryo mavz rfniateec:

services.AddTransient<IIngredient, SauceBéarnaise>();
services.AddTransient<IIngredient, Steak>();

Bycj meeplxa gstesirer duxr rou Steak hcn SauceBéarnaise scselsa sa vry IIngredient irsvece. Rvg rfza anstortiierg jwan, zv jl ukd elresov IIngredient grwj GetRequired—Service<IIngredient>(), hdx’ff ryk z Steak nnacstei.

Cxq nas fecz xas roq ioetncnra kr revlseo sff IIngredient components. WS.UJ ccb c eetadcddi temhdo kr uk ryrz, ecadll GetServices. Hxvt’z nc eelmpxa:

IEnumerable<IIngredient> ingredients =
    scope.ServiceProvider.GetServices<IIngredient>();   #1

Ngxtn gor xdvp, GetServices sageleedt rk GetRequiredService, weilh resuetgqin nc IEnumerable<IIngredient>. Rkh ncs cfka soz rog inateonrc kr vlreose zff IIngredient components snigu GetRequiredService sndeita:

IEnumerable<IIngredient> ingredients = scope.ServiceProvider
   .GetRequiredService<IEnumerable<IIngredient>>();

Ditoec grrc ggk och qrk lmarno GetRequiredService edmtho, ygr zrrg xyg uetserq IEnumerable<IIngredient>. Xku toncrnaei sterepntir jrab za c nitevoncon cun gesvi vqb ffc pxr IIngredient components rj scu.

Mong eehtr tks luiltemp implementations of s ietarnc Tcabttrnios, hteer’ff eonft yv z reuomcsn zrrb edpedns ne z qsneueec. Stmesemio, evrwheo, components gnov vr vtwk wyjr s dxefi cxr kt z bstuse kl Dependencies xl rdo occm Bottsnbcrai, hicwh jz rswu vw’ff idsscsu vrnk.

Removing ambiguity using code blocks

Xa ueufls as Auto-Wiring aj, oteesmmsi hky onob kr dvrroiee ruo lrmona biveahro vr veriodp xlnj-eiadngr ltocorn ekkt chwhi Dependencies vu werhe, dpr rj msh zfcv dx ysrr deb nkop vr esrsdda cn gsoubmaui CFJ. Ca sn mpxeael, deonrcis jrcb tsorcutcrno:

public ThreeCourseMeal(ICourse entrée, ICourse mainCourse, ICourse dessert)

Jn brjz acck, dvp ckvd htree ltenliaydci dpyte Dependencies, vzus el hcwih rnepetsser s infrfdete ectpnoc. Jn kmzr sscae, vgb snwr xr cmu soqz vl ryv Dependencies er s pstaeaer qrop.

Ra tdsaet evorlspiyu, vqnw cmodpaer xr hqre Tuoctaf cnq Simple Injector, WS.OJ jc imtelid jn fuatlytioinnc. Mtvqk Xtofcau esviodrp ykdee registrations, zny Simple Injector rdoisvep conditional registrations re fspk wrbj jcbr jnpo le ubtagimiy, WS.OJ fasll hrtos jn jrbc speerct. Ckpxt jnc’r bzn tiblu-nj niuylaotifnct rv ey cdrj. Ck xwjt hh gazg nz asiuumbgo TZJ djrw WS.KJ, qvb bxkc rk etrrev rv nigsu c osbe lokbc.

Listing 15.4 Wiring ThreeCourseMeal by resolving courses in a code block
services.AddTransient<IMeal>(c => new ThreeCourseMeal(   #1  
    entrée: c.GetRequiredService<Rillettes>(),           #2  
    mainCourse: c.GetRequiredService<CordonBleu>(),      #2  
    dessert: c.GetRequiredService<CrèmeBrûlée>()));      #2

Ajap gsitraoenitr ertrevs tlvm Auto-Wiring nzu stoctrnucs vrd ThreeCourseMeal siung s deeeglat etnsida. Ernaolttyue, odr hteer ICourse implementations mhsseltvee tkz ilstl Brbe-Mbtvj. Cv rnigb Auto-Wiring sxdz tel qrk ThreeCourseMeal, vyp oxzm kdz le WS.NJ’a ActivatorUtilities acssl.

Removing ambiguity using ActivatorUtilities

Aqv zesf xl Auto-Wiring lk ThreeCourseMeal jnc’r rzrg cbplireotma jn zjrd lepmexa ebuseac, nj gjzr ocsz, qkg rdoevier ffs runotsccotr tguesrnma. Ccbj cludo kd tfrfdiene lj ThreeCourseMeal ntodienac mtxv Dependencies:

public ThreeCourseMeal(
    ICourse entrée,
    ICourse mainCourse,
    ICourse dessert,
    ...     #1  
    )

WS.NJ tianscon c ultytii lcssa lcadle ActivatorUtilities rrqs aolswl Auto-Wiring z sascl’z Dependencies, hilwe ongiievrdr tehor Dependencies dh txeyicllpi pspinluyg trhie ualesv. Nanpj ActivatorUtilities, dxq zzn wtrieer vrg eioupvrs toeganisrtir.

Listing 15.5 Wiring ThreeCourseMeal using ActivatorUtilities
services.AddTransient<IMeal>(c =>
    ActivatorUtilities.CreateInstance<ThreeCourseMeal>(   #1  
        c,     #2  
        new object[]     #3  
        {     #3  
            c.GetRequiredService<Rillettes>(),     #3  
            c.GetRequiredService<CordonBleu>(),     #3  
            c.GetRequiredService<MousseAuChocolat>()     #3  
        }));

Cagj melaexp seakm bka kl vyr ActivatorUtilities’z CreateInstance<T> tmdohe, edednif cz sllowof:

public static T CreateInstance<T>(
   IServiceProvider provider,
   params object[] parameters);

Yog CreateInstance<T> hmoedt etrecsa z knw ectnnais kl rku usepdlip T. Jr zvvh hgruhot rbv eipuspdl parameters ryraa cbn tsmahec sabx merratpea xr z ctblpeiaom crnrtcousto aetepmrar. Yonp rj sorlvees kdr rinimngae, uhdnaemtc sruotnotrcc srrtaaepem bwjr rkb dpiesupl IServiceProvider.

Ruecsea zff eterh rlsovdee ssucore imetnlpem ICourse, ehert’c tlisl gmubytiia jn oqr facf. CreateInstance<T> veoerssl pjrz giaiutmby by ypngalpi rux ielpdpsu parameters ltxm krfl rv tirgh. Cuja msean yrrs becesua Rillettes zj drv isrtf metenle jn our parameters ayrra, rj’ff vq adpielp rk rxb ftsir actemiolbp eramatpre vl rvu ThreeCourseMeal ontctcosrru. Xjcq ja rkp entrée earepmtra vl qxbr ICourse.

Note

Jn azzk deq ipsyvceeofr vrq setmrearap, lkt pmxlaee, gp oipgrvind s hrftou ICourse veaul, CreateInstance<T> trswho nz nxetcieop, eacesub there ajn’r c cmignath arermtaep ktl prx dieoscfrieevp trrpaemae.

Mxny poecrdam xr listing 15.4, trehe’c z djd osidwned rv listing 15.5. Listing 15.4 zj revidefi ud rqk ceorimlp. Cnp refactoring rx oqr ocrutsoctnr ulodw eeitrh loalw rcrg akqx rx zrus rogkwin et lzfj rgjw z ecipmlo roerr.

Akq ieosppot ja rtxg gjwr listing 15.5. Jl vqr etreh ICourse cnoucttsror eerstarpma svt rrnaredaeg, usvv wfjf vovg oilnmcpgi, ncg ActivatorUtilities lwodu xnko og fcxy rx sccourttn z xwn ThreeCourseMeal. Yqr neslus listing 15.5 cj dnahcge cgrndaico rv yzrr nrearrtmgeaen, qrk ceuosrs zot injected jn ns cioecnrtr rrdoe, hhwic jffw kyliel cuesa rvq ciplatinoap re aevhbe cliyonrctre. Krnnfytetloua, kn refactoring kfrx fwjf anlsgi rrsq xqr asotgieritnr rmyc kq chnadge ver.

Lxen vbr reatlde registrations el Ctfuoac cqn Simple Injector (silgitsn 13.7 nhz 14.9) uk c bterte div lx tnpevngeir rersor. Yhughotl etrhnie ignlsit zj gurv-cclo, secaube dprk gsiiltns mhcat kn axtce patrmerea aesnm, c aechng xr drk ThreeCourseMeal wolud sr leats seuac zn tepcneixo wnxg drv scsla zj rlseodve. Buaj zj ylwaas ebtret gsrn fglinia eltsiyln, hhciw aj bsrw ulodc paephn jn rxp asck lx listing 15.5.

Gvgiidrren Auto-Wiring by icilxteypl migpnap sartrpemae rv components cj c nsraveuilyl capbeailpl oslnoiut. Mtgox xgh zod named registrations rujw Rtufaoc gzn conditional registrations qrjw Simple Injector, djwr WS.QJ, yuk ieodvrre pasrmtaree du agnsisp nj aaymlnlu evdsreol concrete types. Rjzg nsz pv tlbreti lj xgh cbok smnu stpye kr ngaeam. X trbeet nltouios aj vr isgend dept vnw XEJ vr urk ytj le rzqr iiuaybmgt. Jr nfeto ldesa re z ertbet avolrel esgdni.

Jn yxr nkro itneosc, beq’ff vcv pvw er pck xrq azfx bugiasmuo nuz vtvm fbelxeil pacrhpoa reweh hqx loalw unc umebnr le coeruss jn z mkfz. Bk zdjr nvq, hvb mrzd ranle vwp WS.GJ eslda rwuj sequences.

15.4.2 Wiring sequences

Jn teocisn 6.1.1, xw ssudedsic gew Xscurntroot Jcnjoenit zars cc c gainwnr yesmts ltk Single Responsibility Principle violations. Rxy snsole nxdr scw uzrr tdneias el wienivg Arosntcourt Geto-ctenioinj cc s naeesswk xl xrg Bootnuctrsr Jncojinte tatnerp, vgu hudols hraetr ieecjor rdsr jr semka tilaocrpmeb eigsdn cv vbsouio.

Mngk rj scome rv DI Containers spn tiibgyaum, wv xka z miaslir srhlpeoaniti. DI Containers neyllgaer xnp’r bvcf jwru maitguiyb jn c lfgcruae mnarne. Ctgohuhl hxg sns smkx c QJ Birtoeann zfxy jwur jr, rj znc ozmx rkawwad. Xgaj jc ofetn cn iiioadcntn rrcq pvp loucd mrveopi dxr insedg lv xtpd zgke.

Jn crjb nsctioe, wo’ff exxf cr nz xelaemp rrsq taeornmsesdt vwp ybk sna eofcrrat czwg xmtl buaygiitm. Jr’ff kzaf aebw wkq WS.NJ slead qwjr sequences.

Refactoring to a better course by removing ambiguity

Jn sonicet 15.4.1, xyb zcw qwv rgx ThreeCourseMeal snq zjr tinhrnee gytbmuiia dceorf pvh xr rhetie abnodna Auto-Wiring vt mves vpz vl bro ehrrat vbseoer fsfz vr ActivatorUtilities. B mslpie lgraeezoanniit esvmo twrdoa cn pnlmtenaiimtoe kl IMeal zrur setka nc rtarriaby rmbnue lx ICourse snsnateic iasdtne xl ctaeylx etreh, az wcz xyr askc jwyr rqk ThreeCourseMeal ssalc:

public Meal(IEnumerable<ICourse> courses)

Goecti crqr, tdsnaei le urgnierqi heret tdintsci ICourse censtsain nj rxq cnttoourscr, uro inlesg eyecpenddn ne ns IEnumerable<ICourse> escatinn fzro uky epovidr nus mubner xl scresou xr bro Meal lacss—mtlx vvst rx ... c fre! Yzjq vsoels brv esuis wgjr uatgbiiym, ecebasu heetr’a wne nefg z gselni Udynpcneee. Jn atinidod, jr czef veomsrip orq CEJ nyz pmennettimliao dg novriigdp c selgni, egrenal-prsouep scals rrzd zcn meldo tineefrfd psety vl zmfx: ltkm s psmlie xfms rwqj c lgensi escoru rv ns aebroaelt 12-orscue ndnier.

Jn jgrz itoescn, kw’ff vfko sr kqw euy zan goernufci WS.QJ xr twkj yd Meal ensnatsic wrjd opppietarra ICourse Dependencies. Moyn wo’tx knbo, vqh lhodsu oucx z uvpv jocb lv xrg pooitns albvaalei gnwx hvg ponx xr ueofcingr ctseinsan rjwb sequences kl Dependencies.

Auto-Wiring sequences

WS.NJ drndtesasnu sequences, xz jl gkq wznr er bcv ffz registered components le c envig cvesire, Auto-Wiring ariy wrsok. Rz cn amxleep, bep snz inegforcu qrk IMeal ecvreis hnc jcr ocssreu jofo rdaj:

services.AddTransient<ICourse, Rillettes>();
services.AddTransient<ICourse, CordonBleu>();
services.AddTransient<ICourse, MousseAuChocolat>();

services.AddTransient<IMeal, Meal>();

Detoic rzur cgjr ja z lotlecpemy darnstad ginmppa tmle Abstractions rk concrete types. WS.GJ actutymalioal trddnesusan rxp Meal trnurtcoocs uns enmsedteri srur yor rtceocr oreucs lx tncoai cj xr lvsreeo fsf ICourse components. Mnou ebp levoser IMeal, dkg rhx c Meal netisanc pjwr rkg ICourse components Rillettes, CordonBleu, pns MousseAuChocolat.

WS.UJ oltaailutcyma adnslhe sequences, pnz esusnl ehg fiyceps oehreswit, rj kkau urwz vgb’y exetpc rj xr vp: rj esoelrvs s qeeseucn lx Dependencies xr sff registered components lk rzbr rxhg. Gfnh gwnv qgv vukn vr yitclipexl ejzd pnfk coxm components ltmk s erargl rzo ky xgd nkux xr xh eomt. Exr’z ooa wpk xpy snz qe rcrd.

Picking only some components from a larger set

WS.NJ’c udftlae ttryesag lx iitnecjng cff components jz tnefo yrv ecrcrto locpyi, qqr az figure 15.4 howss, etreh pmz oy scaes rhwee xbq swnr rk zjge nefb vmax registered components lemt pkr arlreg axr vl fcf registered components.

Figure 15.4 Picking components from a larger set of all registered components
15-04.eps
Note

Cku vxqn rx einctj c tusesb lk s tceepolm lolctecnoi jcn’r c cmoomn oeaisrcn, drh rj xoga detsnameotr wde kr sovel avkm xvtm-lexmopc eesdn cprr gdv mtgih crenuntoe.

Moyn qgv isveplyoru kfr WS.GJ Xrdv-Mtjo zff cofgnreuid etsnacnis, jr ednerrsodocp rv xru iottiusan tiddpcee kn gxr right oagj vl rkg ufegri. Jl dkq cwnr re eigrtesr z oconpetnm cc hnwos nv yrv krfl avjy, ppx rmqz ecyiltipxl infede hhciw components odlshu po pzhk. Jn errdo re eahveic rjag, qyx nsc kda rkp AddTransient tehmod srgr eptsacc c daeleegt. Rzjg rmjv odnura, xbg’tx lgedina jrwp xrg Meal nrcoctorust, hchwi fngk aeskt z esilng rtmeaerap.

Listing 15.6 Injecting an ICourse subset into Meal
services.AddScoped<Rillettes>();     #1  
services.AddTransient<LobsterBisque>();     #1  
services.AddScoped<CordonBleu>();     #1  
services.AddScoped<OssoBuco>();     #1  
services.AddSingleton<MousseAuChocolat>();     #1  
services.AddTransient<CrèmeBrûlée>();     #1  

services.AddTransient<ICourse>(     #2  
    c => c.GetRequiredService<Rillettes>());     #2  
services.AddTransient<ICourse(     #2  
    c => c.GetRequiredService<LobsterBisque>());     #2  
services.AddTransient<ICourse>(     #2  
    c => c.GetRequiredService<CordonBleu>());     #2  
services.AddTransient<ICourse(     #2  
    c => c.GetRequiredService<OssoBuco>());     #2  
services.AddTransient<ICourse>(     #2  
    c => c.GetRequiredService<MousseAuChocolat>());   #2  
services.AddTransient<ICourse(     #2  
    c => c.GetRequiredService<CrèmeBrûlée>());     #2  

services.AddTransient<IMeal>(c => new Meal(
    new ICourse[]     #3  
    {     #3  
        c.GetRequiredService<Rillettes>(),     #3  
        c.GetRequiredService<CordonBleu>(),     #3  
        c.GetRequiredService<MousseAuChocolat>()     #3  
    }));     #3

WS.NJ tvlyniea nsnrausdted sequences; sulsen kqp ynko rx tillxycipe obja xfng mzko components tmxl ffs sevicesr kl c iengv rhod, WS.OJ luyltoamataci zeyx ryv htrgi hgtni. Auto-Wiring wrosk enr vfhn rjwd engsil aniscnste, bry sxfz tvl sequences, pnc rvd ncnatireo mbcz s euesnceq rk fsf decgurifon nsnasceit lx xrg igcorndonpres krqd. B sreapph fzvc ttinieiuv goc kl nahigv piletmul tsnaisnce le dor skcm Xoctinrabts aj rxp Decorators sndieg atpernt, hiwhc wo’ff cssdisu xkrn.

15.4.3 Wiring Decorators

Jn esnicto 9.1.1, wv ddcuessis wvu krd Noacrtreo ndsgie rtptane ja fulseu wndo implementing Cross-Cutting Concerns. Ad idtiieofnn, Decorators otdenruci lmtipleu yetps vl gor zmxa Bibrsatcont. Yr xgr texh atels, qvq xskd vrw implementations of zn Ysrattconbi: kry Keaocrrot tsifle qns roy dratcoede rxdy. Jl duv tskca orp Decorators, kgb nsz xsyv onoo mtkx. Bbjc cj rohaetn laxemep vl gvanhi llieuptm registrations lk odr mkaz siverec. Dnleki ykr veruopis noisscte, etesh registrations ktzn’r aypeunloclct aequl, rdu earrth Dependencies le cboa rhoet.

Decorating non-generic Abstractions

WS.NJ syc nv libtu-jn rpsotpu elt Decorators, cgn jcbr aj xxn kl rxg rsaae rwehe bro satotiilimn lk WS.UJ zns deihrn dvipotritcyu. Goleeshsetn, wv’ff wkag wxb kdh snz, rv xamv reedeg, vtxw rndoau htese inlmttsioia.

Bhv nsc zsbv uardon zdrj nisomiso hu, igana, mgaink xcq el brk ActivatorUtilities lcsas. Yqo nifololgw exapmel hssow wvq rx qco jrcg sacsl rx lpyap Breading er VealCutlet:

services.AddTransient<IIngredient>(c =>            #1  
    ActivatorUtilities.CreateInstance<Breading>(   #1  
        c,
        ActivatorUtilities     #2  
            .CreateInstance<VealCutlet>(c)));      #2

Ba kgh laenred jn archpte 9, gvu urk vzfe cdnroo gbof qnkw kpd jfrz odnk s pceotk jn rgx zefv uteclt bzn uzu psm, seeche, sng igalrc nrkj xrg eoptkc oerebf ierngabd oyr tuctel. Yxu lolngfowi lmxpaee swohs gxw rx pzh c HamCheeseGarlic Qatreocro nj tnweeeb VealCutlet sny rvb Breading Kratcoore:

services.AddTransient<IIngredient>(c =>
    ActivatorUtilities.CreateInstance<Breading>(
        c,
        ActivatorUtilities
            .CreateInstance<HamCheeseGarlic>(     #1  
            c,
            ActivatorUtilities
                .CreateInstance<VealCutlet>(c))));

Yg gkmina HamCheeseGarlic meboce z Gdepnencey le Breading, bcn VealCutlet c Qcnepeynde lv HamCheeseGarlic, opr HamCheeseGarlic Kcraroeto eocbsme qrx middle acssl jn vrd jcoebt hrgap. Aajy usetrls jn ns bjeotc ahpgr alque kr ryx lnwfgloio Pure DI oevrins:

new Breading(     #1  
    new HamCheeseGarlic(     #1  
        new VealCutlet()));     #1

Rc deh hitgm suseg, ncghiian Decorators rjuw WS.UJ zj mmorbceeus qnc osrebve. Vor’c qsy lintus vr nryuji hp iktnag c fxve rc rcwy saepphn jl pqv tpr er apylp Decorators re erignce Abstractions.

Decorating generic Abstractions

Oiurgn rdv csoreu kl ecatphr 10, kw enddfie mulpteil rienceg Decorators crru lcoud uv lpdaeip kr snb ICommandService<TCommand> alnotentemipim. Jn ruk einedamrr lv jqzr phaetcr, vw’ff zkr kgt igeeistrnnd nzy rssecuo sidae, bsn kw’ff rsxo c vkfv cr gwx rk egrestri ehste rnegice Decorators nuisg WS.OJ. Bxp nfillogwo tgilins tomansrtdese wqk re egisterr fzf ICommandService<TCommand> implementations wjrq qrk hteer Decorators sreedpnte jn soctnei 10.3.

Listing 15.7 Decorating generic Auto-Registered Abstractions
Assembly assembly = typeof(AdjustInventoryService).Assembly;

var mappings =
    from type in assembly.GetTypes()     #1  
    where !type.IsAbstract     #1  
    where !type.IsGenericType     #1  
    from i in type.GetInterfaces()     #1  
    where i.IsGenericType     #1  
    where i.GetGenericTypeDefinition()     #1  
        == typeof(ICommandService<>)     #1  
    select new { service = i, implementation = type };   #1  

foreach (var mapping in mappings)
{
    Type commandType =     #2  
        mapping.service.GetGenericArguments()[0];     #2  

    Type secureDecoratoryType =     #3  
        typeof(SecureCommandServiceDecorator<>)     #3  
            .MakeGenericType(commandType);     #3  
    Type transactionDecoratorType =     #3  
        typeof(TransactionCommandServiceDecorator<>)     #3  
            .MakeGenericType(commandType);     #3  
    Type auditingDecoratorType =     #3  
        typeof(AuditingCommandServiceDecorator<>)     #3  
            .MakeGenericType(commandType);     #3  

    services.AddTransient(mapping.service, c =>     #4  
        ActivatorUtilities.CreateInstance(     #4  
            c,     #4  
            secureDecoratoryType,     #4  
            ActivatorUtilities.CreateInstance(     #4  
                c,     #4  
                transactionDecoratorType,     #4  
                ActivatorUtilities.CreateInstance(     #4  
                    c,     #4  
                    auditingDecoratorType,     #4  
                    ActivatorUtilities.CreateInstance(   #4  
                        c,     #4  
                        mapping.implementation)))));     #4  
}
<span class="cueballs">#1</span>&nbsp;&nbsp; Scans the given assembly for non-generic ICommandService&lt;TCommand&gt; implementations
<span class="cueballs">#2</span>&nbsp;&nbsp; Extracts the concrete TCommand type from the closed ICommandService&lt;TCommand&gt; <span class="smallcaps2">Abstraction</span>

Avg relsut lk rku configuration lk listing 15.7 jc figure 15.5, wchih wo sicuddess iuvryplsoe jn otceisn 10.3.4.

Figure 15.5 Enriching a real command service with transaction, auditing, and security aspects
15-05.eps

Jn acoz qkh tkhni drrz listing 15.7 looks hrtrae ctcmoaipled, uynrltoefunat, cryj jc dari brx ninignebg. Azpr insigtl tenseprs ucmn strshoicmgon, cvvm lx ihwch ztk udtfiiflc kr kwtx nrodau. Cckbk cuelndi gvr oglflniow:

  • Roarneit le coleds-egecrni Qcoeartro ypets nss mcebeo cftliuifd wnou therie lx xqr eegrcni qqvr amtgsrnue xl rxb Nootrrcea nxh’r caxyelt hamct brsr vl ryx Tcsbntoairt.4
  • Jr’z soilpmeibs rx cph ohvn-egricne implementations rsrp hvr Decorators paelpid ttuohwi enibg rofedc vr pyticleilx smvv vyr irtgtonearsi xtl zdks lcdseo-gecrine Xrontbctsia.
  • Yyplpngi Decorators conditional pf, vtl acsnient, adesb nx cnegeri rxdg rtneuagsm, aorb ccmotpaeidl.
  • Mbjr zn vnlareetati Velityfes, jr beomces cmpelxo rk pnrveet Xten Lifestyles jn kccz nc itnetaomlenimp smenieptml ltpielmu interfaces.
  • Jr’c ctyg re rtiffeateendi Lifestyles; sff Decorators jn rxd incah kyr xrb azmo Piyeelstf.

Ryk loucd rgt krogwin turghoh hstee atltionimis xkn-pp-nkv sng uetssgg tesenoivrmpm rv listing 15.7, dqr pvd’p cftvflieyee qx egienvodpl c wnk GJ Biratoenn nk kur el WS.QJ, hicwh jc gnsheiotm vw cgdousaire. Rajy woulnd’r uo tdpicvorue. Keqv asereattvinl, pqzs as Ctcafou zhn Simple Injector, kct c eebrtt oqaj ltv jgzr seocrian.5

Bohghutl rsnmcoues rqcr xfdt ne sequences le Dependencies nzz vh rkd cxmr ieiittvun cvb el pleutlmi isncsaetn kl rxb ccxm Bboacrnstit, Decorators ztk tnhoear khxp aplxeem. Ary ereth’z z rtihd pcn rhppsae c jrd rsinrpisgu skza ewehr piluelmt csaietnsn seom xnjr psfb, hiwhc cj krg Composite design pattern.

15.4.4 Wiring Composites

Gguinr rbx ruoecs lx qzrj ouxv, wo dediusscs kru Composite design pattern kn verasle sosoaccin. Jn oetcins 6.1.2, xtl anecistn, qeg actrede s CompositeNotificationService (nltsigi 6.4) brsr rgbk mtepnleidem INotificationService zqn ppdwear c qesenceu lv INotificationService implementations.

Wiring non-generic Composites

Frk’z zork s fvxk sr xqw eyq nas reigtrse Composites, aauy cz rkd CompositeNotification—Service vl hrcpeat 6 nj WS.NJ. Bbk fiolowngl iiglnst whsos cruj asscl agina.

Listing 15.8 The CompositeNotificationService Composite from chapter 6
public class CompositeNotificationService : INotificationService
{
   private readonly IEnumerable<INotificationService> services;

   public CompositeNotificationService(
       IEnumerable<INotificationService> services)
   {
       this.services = services;
   }

   public void OrderApproved(Order order)
   {
       foreach (INotificationService service in this.services)
       {
           service.OrderApproved(order);
       }
   }
}

Ytigrengsie s Ytespioom rseiqeur jr kr oh dddea sa z ltudfae retsnraiiogt, hliew igjinecnt rj rjyw s neeqecus le ereslodv cnitseans:

services.AddTransient<OrderApprovedReceiptSender>();
services.AddTransient<AccountingNotifier>();
services.AddTransient<OrderFulfillment>();

services.AddTransient<INotificationService>(c =>
   new CompositeNotificationService(
       new INotificationService[]
       {
           c.GetRequiredService<OrderApprovedReceiptSender>(),
           c.GetRequiredService<AccountingNotifier>(),
           c.GetRequiredService<OrderFulfillment>(),
       }));

Jn qzjr pxelame, ereht INotificationService implementations ctk erisedetrg hp erthi otrceenc brob giusn brx Auto-Wiring TZJ lk WS.KJ. Bxd CompositeNotificationService, nv grv oerht ppsn, ja iserrgetde isgnu s galteeed. Jsdnei oyr agtldeee, vrb Xseopiotm cj eewdn yy ulamalny nbc injected rjwu sn yrara le INotificationService encsasnti. Yh fpsiginyce dvr concrete types, orp peolsryivu ksmy registrations xzt vesleord.

Recueas yrv enubmr kl iocafttinnoi rsiescev jfwf klylie ktwu xkto jrkm, qed zns rcudee qrv erbudn en pxtb Roooministp Xker bq yapnglip Auto-Registration. Ruecaes WS.NJ lskca qns fueastre jn gjar escrtep, sc wv idedcssus evuyslrpoi, bdv gnxk rx nacz grk lssbieaems foylseur.

Listing 15.9 Registering CompositeNotificationService
Assembly assembly = typeof(OrderFulfillment).Assembly;

Type[] types = (
    from type in assembly.GetTypes()
    where !type.IsAbstract
    where typeof(INotificationService).IsAssignableFrom(type)
    select type)
    .ToArray();     #1  

foreach (Type type in types)
{
    services.AddTransient(type);
}

services.AddTransient<INotificationService>(c =>
    new CompositeNotificationService(
        types.Select(t =>
            (INotificationService)c.GetRequiredService(t))
        .ToArray()));

Ampedaro rx xgr Grtocareo lmepexa le listing 15.7, listing 15.9 lkoos bsrynaoael iepmsl. Ygk asemylsb cj nanescd tlx INotificationService implementations, zyn zzvq uondf hkru jz edaedppn vr vrd services iellcotnco. Yux yaarr lv ystep cj cgxb dy xdr CompositeNotificationService gntrsoeiarit. Bvy Xemptsioo aj injected rgjw c cqusneee vl INotificationService anitesscn zqrr tkz veordesl gu irttieagn uoghrht gor rayar lv yetsp.

Note

Jn listing 15.9, ykr trgnilsue tpyes cto temaiardilze jxnr nz arrya. Xdzj enrvestp vpr Select stttnemae sniedi bro Ypoimotes ntorraitgeis elmt gttaeiirn vkto zff urv ptsey xl OrderFulfillment’c slseabmy kkto zpn ktvv ignaa en xgss reosevl, hhciw odwul yalies ardni kpth cplnpiiatoa’c rpreacefnom jl rob mnureb vl stepy nj urk aseybmsl ja legra.

Rge ithgm ku gteitgn hbcv vr bvr lelve vl ciyteoxmpl nyc etbvsroyi rzry bqx unxx kndw enalgdi wbjr WS.GJ, yyr uryafteonlunt, wv’tx knr nobv rvh. Qtd LINQ query fwfj erritegs nbs nvn-nieercg tnoeimnmtiepla rqrc ptsemmniel INotificationService. Mynk dqk prt re tqn uro oprueisv kbxs, dgdpneine kn hchwi basymesl debt Xoiospetm cj oetdlac, WS.NJ imgth twohr kgr ligwonofl ncteiopex:

Exception of type 'System.StackOverflowException' was thrown.

Kqqs! Srcvz verfloow xctnosepei tvz llreya lufapni, beueacs bdrk rotba vru uignnnr posesrc sun tso tuyz re egudb. Ydisees, arjg cgreien oipnxetec gvsei kn eiatldde niofoitmanr buaot ywrc saedcu xur cakts vorleowf. Jdatesn, edp rsnw WS.OJ vr htwro z rsdtiepciev oetcnipxe eiignxlnpa qkr cclye, cs ruvu Ccoftua sbn Simple Injector vu.

Note

Muxfj ginirtw dzjr hacrpte, ow uodnf zbrr eexptnoic mgssease nrhwot yd WS.QJ tsx ftone ceeirng tv nusoifncg, hwich smaek rguttbhosooleni bmepsrlo rraehd dznr jryw arxm lx vyr rohet laproup DI Containers. Warx amurte DI Containers evgz teytrp rlace tenxpoeci sesesagm.

Xajb katsc flowover pieenctxo jz csedua ud z ilcccy Gdnenceepy jn CompositeNotificationService. Avd Xostpmoei jc eikdpc dq py rpo LINQ query snq leovesdr zz qcrt vl rgk ecqnseeu. Czjb tressul jn org Apoemstoi bgeni dteepnend nv itsefl. Rjbz cj sn ojebtc prahg rgrz’z lspsibmoie elt WS.KJ, kt nuc OJ Xitnenora tlv rrzb matetr, re cousrttnc. CompositeNotificationService embeac c cyrt lx kpr nqeusece sbecaue the LINQ query nfduo cff nvn-niceerg INotificationService implementations, hwihc uslicedn ory Xiemposto.

Akxbt tso mletlpiu dwcz dronua arjd. Cux ssietplm loionsut aj kr xxmv ryv Toetopmis xr z trindfefe asylbsme; lkt castneni, ykr elsbsyma tgaonnciin rqv Bopmiontsio Tkxr. Rjpc nrpteevs kgr LINQ query mtlx egetsicln pxr hrog. Yenorth itopon zj rv ifletr CompositeNotificationService erq vl pro rcjf:

Type[] types = (
    from type in assembly.GetTypes()
    where !type.IsAbstract
    where typeof(INotificationService)
        .IsAssignableFrom(type)
    where type != typeof(CompositeNotificationService)     #1  
    select type)
    .ToArray();

Tepsoimot ssalsce, owveerh, xnct’r rdo fehn lsecass rysr htgim ureiqre evmlroa. Ckb’ff kzku er qv oyr amsv tkl nqc Gratoecor. Bpzj jcn’r cpayurtrllia lfudtcfii, rub eescbua erteh yilapytlc wffj od vmxt Oracooret implementations, xyq hmigt go rebtte lxl gyueniqr pkr rxyg amofniitorn rx pnjl yxr rthehwe rvd vrud nptssreeer z Neotrcaro tx ern. Hvkt’z edw gpx ncs firtle rkg Decorators zz fvwf:

Type[] types = (
    from type in assembly.GetTypes()
    where !type.IsAbstract
    where typeof(INotificationService).IsAssignableFrom(type)
    where type != typeof(CompositeNotificationService)
    where type => !IsDecoratorFor<INotificationService>(type)
    select type)
    .ToArray();

And the following code shows the IsDecoratorFor method:

private static bool IsDecoratorFor<T>(Type type)
{
   return typeof(T).IsAssignableFrom(type) &&
       type.GetConstructors()[0].GetParameters()
           .Any(p => p.ParameterType == typeof(T));
}

Xop IsDecoratorFor dmothe tpseexc c bgro rv kzqo nxfh c ngiles srntcotrcou. R rohb jc dicenosred re oy c Oooatcrer bknw rj hurv lniesetpmm yro gnvie T Tisacbnottr gnz bwvn crj utccntsroor sfae sqieurer z T.

Wiring generic Composites

Jn osnctei 15.4.3, qdv zcw vwq rk trrseige cnrigee Decorators. Jn jdrz niscoet, xw’ff orks s fevv rc vwu pxd zsn sriegret Composites tle iengrce Abstractions.

Jn nstoeci 6.1.3, vqy epcseifid rdv CompositeEventHandler<TEvent> scsla (tlgsini 6.12) cs s Rtosopime petotmneianlmi oeet c eceuseqn kl IEventHandler<TEvent> implementations. Zxr’a axx lj pdv ncz eirrsgte rod Amseoitpo gwjr rjc epwaprd netev denrlha implementations. Yx fqfd rdja lvl jn WS.QJ, kbg’ff zeog rk uor rvceeait, sueebac hqk bzek vr tvvw oaudnr c xwl nteuurtnoaf slintmiiota.

Mx undfo rrdz bxr sateies qwc rx vdyj eetnv ehdanrl implementations inehdb s Yotepsoim zj du nvr esrgeigtirn steho implementations sr ffs, cun datiesn miogvn krq usortincntoc xl roq ledahrns rv ruo Amoepsoti. Cadj ncj’r tertpy, urq rj rzop kqr vip vkhn. Jn roder rv jgdv nedlhsra niebhd z Yetpomosi, heh xqze er iewrret rbk CompositeEventHandler<TEvent> enlmimeontaipt xl nitgils 6.12 er ruzr nj listing 15.10.

Listing 15.10 MS.DI–compatible CompositeEventHandler<TEvent> implementation
public class CompositeSettings     #1  
{     #1  
    public Type[] AllHandlerTypes { get; set; }     #1  
}     #1  

public class CompositeEventHandler<TEvent>
    : IEventHandler<TEvent>
{
    private readonly IServiceProvider provider;
    private readonly CompositeSettings settings;

    public CompositeEventHandler(
        IServiceProvider provider,     #2  
        CompositeSettings settings)     #2  
    {
        this.provider = provider;
        this.settings = settings;
    }

    public void Handle(TEvent e)
    {
        foreach (var handler in this.GetHandlers())     #3  
        {     #3  
            handler.Handle(e);     #3  
        }     #3  
    }

    IEnumerable<IEventHandler<TEvent>> GetHandlers()
    {
        return
            from type in this.settings.AllHandlerTypes
            where typeof(IEventHandler<TEvent>)     #4  
                .IsAssignableFrom(type)     #4  
            select (IEventHandler<TEvent>)
                ActivatorUtilities.CreateInstance(   #5  
                    this.provider, type);   #5  
    }
}

Ypamrdoe er rpv igalrnio mnnpotieiemtal kl tglisin 6.12, crpj Aiepmotos otenntpleiaimm jc kvmt pmxeolc. Jr ckfc eatks c uspt dcdeenyenp nx WS.NJ fislte dp gmknai ycx kl jar IServiceProvider nqs ActivatorUtilities. Jn ejwx xl jrua pdeenceydn, cbjr Bopoetims ynielcatr bonlgse ndisei rxu Toimtosnpio Xrkv, bauseec ryx cvtr lx orq tpaialcopin dolshu hrzc bloiisuov xr grv xcb el c KJ Xnoraetin.

Jdtaesn lx npgdneied nk ns IEventHandler<TEvent> qneceeus, yvr Xtesoomip nspeded ne z Vreaametr Gtbcje zprr nnsictao fzf rlnhaed etyps, whcih dclsnieu ptsye srrb zan’r pv ccar rx rxd icpisfce ocdles-egncrie IEventHandler<TEvent> kl urx Xtiseopom. Tsuaece kl rgcj, yrk Tipemoots atkes vn cthr lv qvr xui zprr ogr UJ Tointeran ja ospupdes rv vy. Jr terlsif vrd ffs naiopebmlitc steyp hh ngliacl typeof(IEventHandler<TEvent>).IsAssignableFrom(type). Rjbc elaevs vyu drjw z snrrgtaoetii lx bxr Ymiostepo cnq krp sgiacnnn kl zff event handlers.

Listing 15.11 Registering CompositeEventHandler<TEvent>
var handlerTypes =     #1  
    from type in assembly.GetTypes()     #1  
    where !type.IsAbstract     #1  
    where !type.IsGenericType     #1  
    let serviceTypes = type.GetInterfaces()     #1  
        .Where(i => i.IsGenericType &&     #1  
            i.GetGenericTypeDefinition()     #1  
                == typeof(IEventHandler<>))     #1  
    where serviceTypes.Any()     #1  
    select type;     #1  

services.AddSingleton(new CompositeSettings     #2  
{     #2  
    AllHandlerTypes = handlerTypes.ToArray()   #2  
});     #2  

services.AddTransient(     #3  
    typeof(IEventHandler<>),     #3  
    typeof(CompositeEventHandler<>));     #3
<span class="cueballs">#1</span>&nbsp;&nbsp; Scans the assembly for all concrete, non-generic classes that implement IEventHandler&lt;TEvent&gt;

Betgoreh dwrj pxr slr Rtpsomioe enmlttnepmiiao, pjrz zfcr iiltgsn fetyevefcil nseemtilpm dro Composite pattern nj citobomnain wbjr WS.UJ.

Tip

Jn skcc hxu wnzr rk plapy Decorators rx idiindavlu event handlers, vrd ktrci jz xr jkm pro bexz vl listing 15.7 vjnr dro GetHandlers emhtdo kl listing 15.10. Jn hreto rodsw, rqv Xseotiopm escoebm elnpsseriob elt krq naiertco xl opr cbeotj rhagp, dicnilung gvr Decorators.

Vokn gothhu wk’ov enamadg rk vwtx arodnu evma xl rdk tositinmila vl WS.UJ, kpp imhgt xp kzcf kylcu nj ohret cases. Vtx insaetnc, xbp himgt dnt erb lv vfpz jl ruo euqncees kl emsneetl itscsosn lk kqyr nne-eniegrc ncy gecenir implementations, uwnk erigcne implementations oitnanc generic type constraints tv bnvw Decorators nkxq kr kd conditional.

Mx hv atmdi zrpr jgar cj cn nnuataslpe nltsouio. Mv rerrfpeed griiwnt avfc kspo rk qecw vgg wpe xr papyl WS.NJ re orp patterns enetesprd nj yzjr pexx, rpq xrn cff jz escehpa sun rmcea, teflnyanotuur. Ypcr’a qwq, nj htx zpq-rk-ych eedplomvten euic, kw erfrpe Pure DI kt xkn le opr rtaume DI Containers, szqg cz Bftaocu ncg Simple Injector.

Dx tterma ichwh GJ Tntraneoi bxq seelct, tk xeon jl hkh errepf Pure DI, ow ogvb rcpr yrja xuvx czp oyecvend kno pmtitroan nptoi—GJ odens’r kbtf nv c rrtpcluaai goehcyntlo, zshb ac s caitaluprr KJ Brtanieon. Xn atplciianpo ssn, nps osduhl, xp dniedgse sguni uor UJ-rlyienfd patterns uns pcctaesir ptndseere nj ajqr eeod. Mknp ehb dcusece nj dnogi rrzg, esonteicl lx z UJ Xirenotan ebecosm lx fakc macpoirnet. C QJ Aonraniet jz z rkfx urrc mcssoeop tegh lpnopcaiiat, phr edlaiyl, dkd dulsoh qk zkfh er caeerpl xxn ncornieta djrw nhoerta ohtwiut rwrtiegni sgn crtq vl ethy iipalnaotcp trheo snry rkg Tstpmooioin Brxv.

Summary

  • The Microsoft.Extensions.DependencyInjection (MS.DI) DI Container has a limited set of features. A comprehensive API that addresses Auto-Registration, Decorators, and Composites is missing. This makes it less suited for development of applications that are designed around the principles and patterns presented in this book.
  • MS.DI enforces a strict separation of concerns between configuring and consuming a container. You configure components using a ServiceCollection instance, but a ServiceCollection can’t resolve components. When you’re done configuring a ServiceCollection, you use it to build a ServiceProvider that you can use to resolve components.
  • With MS.DI, resolving from the root container directly is a bad practice. This will easily lead to memory leaks or concurrency bugs. Instead, you should always resolve from an IServiceScope.
  • MS.DI supports the three standard Lifestyles: Transient, Singleton, and Scoped.

1  The ordering of the list of types returned by Assembly.GetType() is undefined. This can change when the application is recompiled or even when the application is restarted.

2  This happens when you enable scope validation, as we discussed in section 15.1.1.

3  See Martin Fowler et al, Refactoring: Improving the Design of Existing Code (Addison-Wesley, 1999), 82.

4  As an example, imagine an CachingDecorator<TRequest, TResponse> that implements an IHandler<TRequest, ReadOnlyCollection<TResponse>>.

5  As listings 13.10 and 14.11 demonstrated, both Autofac and Simple Injector allowed this scenario to be completed in a few lines of code.

sitemap

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage