From cdf3d3879e1d5d1d80360cac8f50b30b7b1a1c45 Mon Sep 17 00:00:00 2001 From: z4kn4fein Date: Sat, 14 Dec 2024 11:50:46 +0000 Subject: [PATCH] deploy: bddfe27ae7123011ae0f48386522c0196bd1d982 --- 404.html | 2 +- assets/js/{026a065b.ab3f0196.js => 026a065b.c4544f30.js} | 2 +- assets/js/{115c3c33.0d0b2113.js => 115c3c33.02b13161.js} | 2 +- assets/js/{3a87badd.50d04665.js => 3a87badd.706346b9.js} | 2 +- assets/js/{4047545b.9ec870e0.js => 4047545b.f98d0351.js} | 2 +- assets/js/{5b5f0b93.7ea32b58.js => 5b5f0b93.23961e77.js} | 2 +- assets/js/{61ac6d0c.f2525d28.js => 61ac6d0c.aa2e8309.js} | 2 +- assets/js/{6d4ed487.43c43c69.js => 6d4ed487.af545583.js} | 2 +- assets/js/{74fdf922.0b9b5797.js => 74fdf922.71931827.js} | 2 +- assets/js/{7a96ca3d.7f57d2b9.js => 7a96ca3d.8d38ba06.js} | 2 +- assets/js/{851a7a9e.4e8777b3.js => 851a7a9e.c87840f5.js} | 2 +- assets/js/{9ff4038f.0e0ce6cd.js => 9ff4038f.72dcabb1.js} | 2 +- assets/js/{a1e0d343.44070a57.js => a1e0d343.62369d9d.js} | 2 +- assets/js/{b43f639d.30eb1c65.js => b43f639d.e18adc97.js} | 2 +- assets/js/{b53e7bc5.56104c16.js => b53e7bc5.2ffd6d83.js} | 2 +- assets/js/{dd45a7f1.115c8e6a.js => dd45a7f1.85193944.js} | 2 +- assets/js/{fd379919.463a8430.js => fd379919.6f340728.js} | 2 +- assets/js/{ff10094c.2763c19f.js => ff10094c.ccd23fc1.js} | 2 +- assets/js/runtime~main.7f3eed93.js | 1 + assets/js/runtime~main.b12ec231.js | 1 - docs/advanced/child-containers.html | 4 ++-- docs/advanced/decorators.html | 4 ++-- docs/advanced/generics.html | 4 ++-- docs/advanced/special-resolution-cases.html | 4 ++-- docs/advanced/wrappers-resolvers.html | 4 ++-- docs/configuration/container-configuration.html | 4 ++-- docs/configuration/registration-configuration.html | 4 ++-- docs/diagnostics/utilities.html | 4 ++-- docs/diagnostics/validation.html | 4 ++-- docs/getting-started/glossary.html | 4 ++-- docs/getting-started/introduction.html | 4 ++-- docs/getting-started/overview.html | 4 ++-- docs/guides/advanced-registration.html | 4 ++-- docs/guides/basics.html | 4 ++-- docs/guides/lifetimes.html | 4 ++-- docs/guides/scopes.html | 4 ++-- docs/guides/service-resolution.html | 4 ++-- index.html | 2 +- search.html | 2 +- 39 files changed, 55 insertions(+), 55 deletions(-) rename assets/js/{026a065b.ab3f0196.js => 026a065b.c4544f30.js} (99%) rename assets/js/{115c3c33.0d0b2113.js => 115c3c33.02b13161.js} (97%) rename assets/js/{3a87badd.50d04665.js => 3a87badd.706346b9.js} (99%) rename assets/js/{4047545b.9ec870e0.js => 4047545b.f98d0351.js} (99%) rename assets/js/{5b5f0b93.7ea32b58.js => 5b5f0b93.23961e77.js} (99%) rename assets/js/{61ac6d0c.f2525d28.js => 61ac6d0c.aa2e8309.js} (99%) rename assets/js/{6d4ed487.43c43c69.js => 6d4ed487.af545583.js} (99%) rename assets/js/{74fdf922.0b9b5797.js => 74fdf922.71931827.js} (99%) rename assets/js/{7a96ca3d.7f57d2b9.js => 7a96ca3d.8d38ba06.js} (99%) rename assets/js/{851a7a9e.4e8777b3.js => 851a7a9e.c87840f5.js} (99%) rename assets/js/{9ff4038f.0e0ce6cd.js => 9ff4038f.72dcabb1.js} (94%) rename assets/js/{a1e0d343.44070a57.js => a1e0d343.62369d9d.js} (98%) rename assets/js/{b43f639d.30eb1c65.js => b43f639d.e18adc97.js} (99%) rename assets/js/{b53e7bc5.56104c16.js => b53e7bc5.2ffd6d83.js} (99%) rename assets/js/{dd45a7f1.115c8e6a.js => dd45a7f1.85193944.js} (98%) rename assets/js/{fd379919.463a8430.js => fd379919.6f340728.js} (99%) rename assets/js/{ff10094c.2763c19f.js => ff10094c.ccd23fc1.js} (99%) create mode 100644 assets/js/runtime~main.7f3eed93.js delete mode 100644 assets/js/runtime~main.b12ec231.js diff --git a/404.html b/404.html index 48bd4de8..cc2d1d97 100644 --- a/404.html +++ b/404.html @@ -10,7 +10,7 @@ - + diff --git a/assets/js/026a065b.ab3f0196.js b/assets/js/026a065b.c4544f30.js similarity index 99% rename from assets/js/026a065b.ab3f0196.js rename to assets/js/026a065b.c4544f30.js index 0361ad64..6f7d0177 100644 --- a/assets/js/026a065b.ab3f0196.js +++ b/assets/js/026a065b.c4544f30.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[313],{4576:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>l,default:()=>p,frontMatter:()=>c,metadata:()=>d,toc:()=>h});var i=t(4848),o=t(8453),r=t(7470),s=t(1470),a=t(9365);const c={},l="Service resolution",d={id:"guides/service-resolution",title:"Service resolution",description:"When you have all your components registered and configured adequately, you can resolve them from the container or a scope by requesting their service type.",source:"@site/docs/guides/service-resolution.md",sourceDirName:"guides",slug:"/guides/service-resolution",permalink:"/stashbox/docs/guides/service-resolution",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/guides/service-resolution.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Advanced registration",permalink:"/stashbox/docs/guides/advanced-registration"},next:{title:"Lifetimes",permalink:"/stashbox/docs/guides/lifetimes"}},u={},h=[{value:"Injection patterns",id:"injection-patterns",level:2},{value:"Attributes",id:"attributes",level:2},{value:"Using your own attributes",id:"using-your-own-attributes",level:3},{value:"Dependency binding",id:"dependency-binding",level:2},{value:"Conventional resolution",id:"conventional-resolution",level:2},{value:"Conditional resolution",id:"conditional-resolution",level:2},{value:"Optional resolution",id:"optional-resolution",level:2},{value:"Dependency overrides",id:"dependency-overrides",level:2},{value:"Activation",id:"activation",level:2},{value:"Build-up",id:"build-up",level:3}];function g(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",li:"li",mdxAdmonitionTitle:"mdxAdmonitionTitle",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h1,{id:"service-resolution",children:"Service resolution"}),"\n",(0,i.jsxs)(n.p,{children:["When you have all your components registered and configured adequately, you can resolve them from the container or a ",(0,i.jsx)(n.a,{href:"/docs/guides/scopes",children:"scope"})," by requesting their ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["During a service's resolution, the container walks through the entire ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"})," and instantiates all dependencies required for the service construction.\nWhen the container encounters any violations of ",(0,i.jsx)(n.a,{href:"/docs/diagnostics/validation#resolution-validation",children:"these rules"})," ",(0,i.jsx)(n.em,{children:"(circular dependencies, missing required services, lifetime misconfigurations)"})," during the walkthrough, it lets you know that something is wrong by throwing a specific exception."]}),"\n",(0,i.jsx)(n.h2,{id:"injection-patterns",children:"Injection patterns"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Constructor injection"})," is the ",(0,i.jsx)(n.em,{children:"primary dependency injection pattern"}),". It encourages the organization of dependencies to a single place - the constructor."]}),(0,i.jsxs)(n.p,{children:["Stashbox, by default, uses the constructor that has the most parameters it knows how to resolve. This behavior is configurable through ",(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#constructor-selection",children:"constructor selection"}),"."]}),(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#property-field-injection",children:"Property/field injection"})," is also supported in cases where constructor injection is not applicable."]}),(0,i.jsxs)(n.p,{children:["Members defined with C# 11's ",(0,i.jsx)(n.a,{href:"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required",children:(0,i.jsx)(n.code,{children:"required"})})," keyword are automatically injected by the container.\nThis behavior can be controlled with ",(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#required-member-injection",children:"registration"})," or ",(0,i.jsx)(n.a,{href:"/docs/configuration/container-configuration#required-member-injection",children:"container"})," configuration options"]}),(0,i.jsxs)(n.admonition,{type:"info",children:[(0,i.jsx)(n.mdxAdmonitionTitle,{}),(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/docs/configuration/container-configuration#constructor-selection",children:"Constructor selection"})," and ",(0,i.jsx)(n.a,{href:"/docs/configuration/container-configuration#auto-member-injection",children:"property/field injection"})," is also configurable container-wide."]})]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{children:[(0,i.jsx)(a.A,{value:"Constructor injection",label:"Constructor injection",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n private readonly ILogger logger;\n private readonly IEventBroadcaster eventBroadcaster;\n\n public DbBackup(ILogger logger, IEventBroadcaster eventBroadcaster)\n {\n this.logger = logger;\n this.eventBroadcaster = eventBroadcaster;\n }\n}\n\ncontainer.Register();\ncontainer.Register();\n\ncontainer.Register();\n\n// resolution using the available constructor.\nIJob job = container.Resolve();\n"})})}),(0,i.jsx)(a.A,{value:"Property/field injection",label:"Property/field injection",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n public ILogger Logger { get; set; }\n public IEventBroadcaster EventBroadcaster { get; set; }\n\n public DbBackup() \n { }\n}\n\ncontainer.Register();\ncontainer.Register();\n\n// registration of service with auto member injection.\ncontainer.Register(options => \n options.WithAutoMemberInjection());\n\n// resolution will inject the properties.\nIJob job = container.Resolve();\n"})})})]})})]}),"\n",(0,i.jsx)(n.admonition,{type:"caution",children:(0,i.jsxs)(n.p,{children:["It's a common mistake to use the ",(0,i.jsx)(n.em,{children:"property/field injection"})," only to disencumber the constructor from having too many parameters. That's a code smell and also violates the ",(0,i.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Single-responsibility_principle",children:"Single-responsibility principle"}),". If you recognize these conditions, you should consider splitting your class into multiple smaller units rather than adding an extra property-injected dependency."]})}),"\n",(0,i.jsx)(n.h2,{id:"attributes",children:"Attributes"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(n.p,{children:"Attributes can give you control over how Stashbox selects dependencies for a service's resolution."}),(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Dependency attribute"}),":"]}),(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"On a constructor/method parameter"}),": used with the ",(0,i.jsx)(n.em,{children:"name"})," property, it works as a marker for ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"On a property/field"}),": first, it enables ",(0,i.jsx)(n.em,{children:"auto-injection"})," on the marked property/field (even if it wasn't configured at registration explicitly), and just as with the method parameter, it allows ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"}),"."]}),"\n"]}),"\n"]}),(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"DependencyName attribute"}),": a parameter marked with this attribute will get the related service's dependency name."]}),(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"InjectionMethod attribute"}),": marks a method to be called when the requested service is instantiated."]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{children:[(0,i.jsx)(a.A,{value:"Constructor",label:"Constructor",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n private readonly ILogger logger;\n\n public DbBackup([Dependency("Console")]ILogger logger)\n {\n this.logger = logger;\n }\n}\n\ncontainer.Register("Console");\ncontainer.Register("File");\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})}),(0,i.jsx)(a.A,{value:"Property/field",label:"Property/field",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n [Dependency("Console")]\n public ILogger Logger { get; set; }\n\n public DbBackup() \n { }\n}\n\ncontainer.Register("Console");\ncontainer.Register("File");\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})}),(0,i.jsx)(a.A,{value:"DependencyName",label:"DependencyName",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n public string Name { get; set; }\n\n public DbBackup([DependencyName] string name) \n { }\n}\n\ncontainer.Register("Backup");\n\n\nIJob job = container.Resolve();\n// name is "Backup".\nvar name = job.Name;\n'})})}),(0,i.jsx)(a.A,{value:"Method",label:"Method",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n [InjectionMethod]\n public void Initialize([Dependency("Console")]ILogger logger)\n {\n this.logger.Log("Initializing.");\n }\n}\n\ncontainer.Register("Console");\ncontainer.Register("File");\n\ncontainer.Register();\n\n// the container will call DbBackup\'s Initialize method.\nIJob job = container.Resolve();\n'})})})]})})]}),"\n",(0,i.jsx)(n.admonition,{type:"caution",children:(0,i.jsxs)(n.p,{children:["Attributes provide a more straightforward configuration, but using them also tightens the bond between your application and Stashbox. If you consider this an issue, you can use the ",(0,i.jsx)(n.a,{href:"/docs/guides/service-resolution#dependency-binding",children:"dependency binding"})," API or ",(0,i.jsx)(n.a,{href:"/docs/guides/service-resolution#using-your-own-attributes",children:"your own attributes"}),"."]})}),"\n",(0,i.jsx)(n.h3,{id:"using-your-own-attributes",children:"Using your own attributes"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(n.p,{children:"There's an option to extend the container's dependency finding mechanism with your own attributes."}),(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Additional Dependency attributes"}),": you can use the ",(0,i.jsx)(n.a,{href:"/docs/configuration/container-configuration#withadditionaldependencyattribute",children:(0,i.jsx)(n.code,{children:".WithAdditionalDependencyAttribute()"})})," container configuration option to let the container know that it should watch for additional attributes besides the built-in ",(0,i.jsx)(n.a,{href:"/docs/guides/service-resolution#attributes",children:(0,i.jsx)(n.code,{children:"Dependency"})})," attribute upon building up the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Additional DependencyName attributes"}),": you can use the ",(0,i.jsx)(n.a,{href:"/docs/configuration/container-configuration#withadditionaldependencynameattribute",children:(0,i.jsx)(n.code,{children:".WithAdditionalDependencyNameAttribute()"})})," container configuration option to use additional dependency name indicator attributes besides the built-in ",(0,i.jsx)(n.a,{href:"/docs/guides/service-resolution#attributes",children:(0,i.jsx)(n.code,{children:"DependencyName"})})," attribute."]}),"\n"]}),"\n"]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{children:[(0,i.jsx)(a.A,{value:"Dependency",label:"Dependency",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n [CustomDependency("Console")]\n public ILogger Logger { get; set; }\n\n public DbBackup() \n { }\n}\n\nvar container = new StashboxContainer(options => options\n .WithAdditionalDependencyAttribute());\n\ncontainer.Register("Console");\ncontainer.Register("File");\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})}),(0,i.jsx)(a.A,{value:"DependencyName",label:"DependencyName",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n public string Name { get; set; }\n\n public DbBackup([CustomName] string name) \n { }\n}\n\nvar container = new StashboxContainer(options => options\n .WithAdditionalDependencyNameAttribute());\n\ncontainer.Register("Backup");\n\nIJob job = container.Resolve();\n// name is "Backup".\nvar name = job.Name;\n'})})})]})})]}),"\n",(0,i.jsx)(n.h2,{id:"dependency-binding",children:"Dependency binding"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(n.p,{children:"The same dependency configuration functionality as attributes, but without attributes."}),(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Binding to a parameter"}),": the same functionality as the ",(0,i.jsx)(n.a,{href:"/docs/guides/service-resolution#attributes",children:(0,i.jsx)(n.code,{children:"Dependency"})})," attribute on a constructor or method parameter, enabling ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Binding to a property/field"}),": the same functionality as the ",(0,i.jsx)(n.a,{href:"/docs/guides/service-resolution#attributes",children:(0,i.jsx)(n.code,{children:"Dependency"})})," attribute, enabling the injection of the given property/field."]}),"\n"]}),"\n"]}),(0,i.jsx)(n.admonition,{type:"info",children:(0,i.jsxs)(n.p,{children:["There are further dependency binding options ",(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#dependency-configuration",children:"available"})," on the registration configuration API."]})})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{children:[(0,i.jsx)(a.A,{value:"Bind to parameter",label:"Bind to parameter",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n public DbBackup(ILogger logger)\n { }\n}\n\ncontainer.Register("Console");\ncontainer.Register("File");\n\n// registration of service with the dependency binding.\ncontainer.Register(options => options\n .WithDependencyBinding("logger", "Console"));\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})}),(0,i.jsx)(a.A,{value:"Bind to property / field",label:"Bind to property / field",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n public ILogger Logger { get; set; }\n}\n\ncontainer.Register("Console");\ncontainer.Register("File");\n\n// registration of service with the member injection.\ncontainer.Register(options => options\n .WithDependencyBinding("Logger", "Console"));\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})})]})})]}),"\n",(0,i.jsx)(n.h2,{id:"conventional-resolution",children:"Conventional resolution"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(n.p,{children:"When you enable conventional resolution, the container treats member and method parameter names as their dependency identifier."}),(0,i.jsx)(n.p,{children:"It's like an implicit dependency binding on every class member."}),(0,i.jsx)(n.p,{children:"First, you have to enable conventional resolution through the configuration of the container:"}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .TreatParameterAndMemberNameAsDependencyName());\n"})}),(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["The container will attempt a ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"})," on each dependency based on their parameter or property/field name."]})})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{children:[(0,i.jsx)(a.A,{value:"Parameters",label:"Parameters",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n public DbBackup(\n // the parameter name identifies the dependency.\n ILogger consoleLogger)\n { }\n}\n\ncontainer.Register("consoleLogger");\ncontainer.Register("fileLogger");\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})}),(0,i.jsx)(a.A,{value:"Properties / fields",label:"Properties / fields",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n // the property name identifies the dependency.\n public ILogger ConsoleLogger { get; set; }\n}\n\ncontainer.Register("ConsoleLogger");\ncontainer.Register("FileLogger");\n\n// registration of service with auto member injection.\ncontainer.Register(options => options\n .WithAutoMemberInjection());\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})})]})})]}),"\n",(0,i.jsx)(n.h2,{id:"conditional-resolution",children:"Conditional resolution"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(n.p,{children:"Stashbox can resolve a particular dependency based on its context. This context is typically the reflected type of dependency, its usage, and the type it gets injected into."}),(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Attribute"}),": you can filter on constructor, method, property, or field attributes to select the desired dependency for your service. In contrast to the ",(0,i.jsx)(n.code,{children:"Dependency"})," attribute, this configuration doesn't tie your application to Stashbox because you use your own attributes."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Parent type"}),": you can filter on what type the given service is injected into."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Resolution path"}),": similar to the parent type and attribute condition but extended with inheritance. You can set that the given service is only usable in a type's resolution path. This means that each direct and sub-dependency of the selected type must use the provided service as a dependency."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Custom"}),": with this, you can build your own selection logic based on the given contextual type information."]}),"\n"]}),"\n"]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{children:[(0,i.jsx)(a.A,{value:"Attribute",label:"Attribute",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class ConsoleAttribute : Attribute { }\n\nclass DbBackup : IJob\n{\n public DbBackup([Console]ILogger logger)\n { }\n}\n\ncontainer.Register(options => options\n // resolve only when the injected parameter, \n // property or field has the 'Console' attribute\n .WhenHas());\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n"})})}),(0,i.jsx)(a.A,{value:"Parent",label:"Parent",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n public DbBackup(ILogger logger)\n { }\n}\n\ncontainer.Register(options => options\n // inject only when we are \n // currently resolving DbBackup OR StorageCleanup.\n .WhenDependantIs()\n .WhenDependantIs());\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n"})})}),(0,i.jsx)(a.A,{value:"Path",label:"Path",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n public DbBackup(IStorage storage)\n { }\n}\n\nclass FileStorage : IStorage\n{\n public FileStorage(ILogger logger) \n { }\n}\n\ncontainer.Register(options => options\n // inject only when we are in the\n // resolution path of DbBackup\n .WhenInResolutionPathOf());\n\ncontainer.Register();\ncontainer.Register();\n\n// the container will select ConsoleLogger for FileStorage\n// because they are injected into DbBackup.\nIJob job = container.Resolve();\n"})})}),(0,i.jsx)(a.A,{value:"Custom",label:"Custom",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n public DbBackup(ILogger logger)\n { }\n}\n\ncontainer.Register(options => options\n // inject only when we are \n // currently resolving DbBackup.\n .When(typeInfo => typeInfo.ParentType.Equals(typeof(DbBackup))));\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n"})})}),(0,i.jsx)(a.A,{value:"Collection",label:"Collection",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbJobsExecutor : IJobsExecutor\n{\n public DbBackup(IEnumerable jobs)\n { }\n}\n\ncontainer.Register(options => options\n .WhenDependantIs());\ncontainer.Register(options => options\n .WhenDependantIs());\nontainer.Register();\n\ncontainer.Register();\n\n// jobsExecutor will get DbBackup and DbCleanup within a collection.\nIJobsExecutor jobsExecutor = container.Resolve();\n"})})})]})})]}),"\n",(0,i.jsxs)(n.p,{children:["The specified conditions are behaving like filters when a ",(0,i.jsx)(n.strong,{children:"collection"})," is requested."]}),"\n",(0,i.jsxs)(n.p,{children:["When you use the same conditional option multiple times, the container will evaluate them ",(0,i.jsx)(n.strong,{children:"with OR"})," logical operator."]}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#conditions",children:"Here"})," you can find each condition related registration option."]})}),"\n",(0,i.jsx)(n.h2,{id:"optional-resolution",children:"Optional resolution"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(n.p,{children:["In cases where it's not guaranteed that a service is resolvable, either because it's not registered or any of its dependencies are missing, you can attempt an optional resolution using the ",(0,i.jsx)(n.code,{children:"ResolveOrDefault()"})," method."]}),(0,i.jsxs)(n.p,{children:["When the resolution attempt fails, it will return ",(0,i.jsx)(n.code,{children:"null"})," (or ",(0,i.jsx)(n.code,{children:"default"})," in case of value types)."]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{groupId:"generic-runtime-apis",children:[(0,i.jsx)(a.A,{value:"Generic API",label:"Generic API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"// returns null when the resolution fails.\nIJob job = container.ResolveOrDefault();\n\n// throws ResolutionFailedException when the resolution fails.\nIJob job = container.Resolve();\n"})})}),(0,i.jsx)(a.A,{value:"Runtime type API",label:"Runtime type API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"// returns null when the resolution fails.\nobject job = container.ResolveOrDefault(typeof(IJob));\n\n// throws ResolutionFailedException when the resolution fails.\nobject job = container.Resolve(typeof(IJob));\n"})})})]})})]}),"\n",(0,i.jsx)(n.h2,{id:"dependency-overrides",children:"Dependency overrides"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(n.p,{children:["At resolution time, you can override a service's dependencies by passing an ",(0,i.jsx)(n.code,{children:"object[]"})," to the ",(0,i.jsx)(n.code,{children:"Resolve()"})," method."]}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n public DbBackup(ILogger logger)\n { }\n}\n"})})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{groupId:"generic-runtime-apis",children:[(0,i.jsx)(a.A,{value:"Generic API",label:"Generic API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"DbBackup backup = container.Resolve( \n dependencyOverrides: new object[] \n { \n new ConsoleLogger() \n });\n"})})}),(0,i.jsx)(a.A,{value:"Runtime type API",label:"Runtime type API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"object backup = container.Resolve(typeof(DbBackup),\n dependencyOverrides: new object[] \n { \n new ConsoleLogger() \n });\n"})})})]})})]}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(n.p,{children:["To get more control over your overrides (like giving them name to allow ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"}),"), you can use the ",(0,i.jsx)(n.code,{children:"Override"})," built-in wrapper."]}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n public DbBackup([Dependency("console")]ILogger logger)\n { }\n}\n'})})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{groupId:"generic-runtime-apis",children:[(0,i.jsx)(a.A,{value:"Generic API",label:"Generic API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'DbBackup backup = container.Resolve( \n dependencyOverrides: new object[] \n { \n Override.Of(new ConsoleLogger(), "console"), \n });\n'})})}),(0,i.jsx)(a.A,{value:"Runtime type API",label:"Runtime type API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'object backup = container.Resolve(typeof(DbBackup),\n dependencyOverrides: new object[] \n { \n Override.Of(typeof(ILogger), new ConsoleLogger(), "console"), \n });\n'})})})]})})]}),"\n",(0,i.jsx)(n.h2,{id:"activation",children:"Activation"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(n.p,{children:["You can use the container's ",(0,i.jsx)(n.code,{children:".Activate()"})," method when you only want to build up an instance from a type on the fly without registration."]}),(0,i.jsxs)(n.p,{children:["It allows dependency overriding with ",(0,i.jsx)(n.code,{children:"object"})," arguments and performs property/field/method injection (when configured)."]}),(0,i.jsxs)(n.p,{children:["It works like ",(0,i.jsx)(n.code,{children:"Activator.CreateInstance()"})," except that Stashbox supplies the dependencies."]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{groupId:"generic-runtime-apis",children:[(0,i.jsx)(a.A,{value:"Generic API",label:"Generic API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"// use dependency injected by container.\nDbBackup backup = container.Activate();\n\n// override the injected dependency.\nDbBackup backup = container.Activate(new ConsoleLogger());\n"})})}),(0,i.jsx)(a.A,{value:"Runtime type API",label:"Runtime type API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"// use dependency injected by container.\nobject backup = container.Activate(typeof(DbBackup));\n\n// override the injected dependency.\nobject backup = container.Activate(typeof(DbBackup), new ConsoleLogger());\n"})})})]})})]}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(n.h3,{id:"build-up",children:"Build-up"}),(0,i.jsxs)(n.p,{children:["With the ",(0,i.jsx)(n.code,{children:".BuildUp()"})," method, you can do the same ",(0,i.jsx)(n.em,{children:"on the fly"})," post-processing (property/field/method injection) on already constructed instances."]}),(0,i.jsx)(n.admonition,{type:"caution",children:(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:".BuildUp()"})," won't register the given instance into the container."]})})]}),(0,i.jsx)("div",{children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n public ILogger Logger { get; set; }\n}\n\nDbBackup backup = new DbBackup();\n// the container fills the Logger property.\ncontainer.BuildUp(backup); \n"})})})]})]})}function p(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(g,{...e})}):g(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>s});t(6540);var i=t(870);const o={tabItem:"tabItem_Ymn6"};var r=t(4848);function s(e){let{children:n,hidden:t,className:s}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,i.A)(o.tabItem,s),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>I});var i=t(6540),o=t(870),r=t(3104),s=t(6347),a=t(205),c=t(7485),l=t(1682),d=t(9466);function u(e){return i.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,i.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:t}=e;return(0,i.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:t,attributes:i,default:o}}=e;return{value:n,label:t,attributes:i,default:o}}))}(t);return function(e){const n=(0,l.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function g(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:t}=e;const o=(0,s.W6)(),r=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,c.aZ)(r),(0,i.useCallback)((e=>{if(!r)return;const n=new URLSearchParams(o.location.search);n.set(r,e),o.replace({...o.location,search:n.toString()})}),[r,o])]}function b(e){const{defaultValue:n,queryString:t=!1,groupId:o}=e,r=h(e),[s,c]=(0,i.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!g({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const i=t.find((e=>e.default))??t[0];if(!i)throw new Error("Unexpected error: 0 tabValues");return i.value}({defaultValue:n,tabValues:r}))),[l,u]=p({queryString:t,groupId:o}),[b,j]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[o,r]=(0,d.Dv)(t);return[o,(0,i.useCallback)((e=>{t&&r.set(e)}),[t,r])]}({groupId:o}),x=(()=>{const e=l??b;return g({value:e,tabValues:r})?e:null})();(0,a.A)((()=>{x&&c(x)}),[x]);return{selectedValue:s,selectValue:(0,i.useCallback)((e=>{if(!g({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);c(e),u(e),j(e)}),[u,j,r]),tabValues:r}}var j=t(2303);const x={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var m=t(4848);function v(e){let{className:n,block:t,selectedValue:i,selectValue:s,tabValues:a}=e;const c=[],{blockElementScrollPositionUntilNextRender:l}=(0,r.a_)(),d=e=>{const n=e.currentTarget,t=c.indexOf(n),o=a[t].value;o!==i&&(l(n),s(o))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const t=c.indexOf(e.currentTarget)+1;n=c[t]??c[0];break}case"ArrowLeft":{const t=c.indexOf(e.currentTarget)-1;n=c[t]??c[c.length-1];break}}n?.focus()};return(0,m.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,o.A)("tabs",{"tabs--block":t},n),children:a.map((e=>{let{value:n,label:t,attributes:r}=e;return(0,m.jsx)("li",{role:"tab",tabIndex:i===n?0:-1,"aria-selected":i===n,ref:e=>c.push(e),onKeyDown:u,onClick:d,...r,className:(0,o.A)("tabs__item",x.tabItem,r?.className,{"tabs__item--active":i===n}),children:t??n},n)}))})}function y(e){let{lazy:n,children:t,selectedValue:o}=e;const r=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=r.find((e=>e.props.value===o));return e?(0,i.cloneElement)(e,{className:"margin-top--md"}):null}return(0,m.jsx)("div",{className:"margin-top--md",children:r.map(((e,n)=>(0,i.cloneElement)(e,{key:n,hidden:e.props.value!==o})))})}function f(e){const n=b(e);return(0,m.jsxs)("div",{className:(0,o.A)("tabs-container",x.tabList),children:[(0,m.jsx)(v,{...e,...n}),(0,m.jsx)(y,{...e,...n})]})}function I(e){const n=(0,j.A)();return(0,m.jsx)(f,{...e,children:u(e.children)},String(n))}},7470:(e,n,t)=>{t.d(n,{A:()=>s});var i=t(6540);const o={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var r=t(4848);function s(e){let{children:n}=e,t=i.Children.toArray(n).filter((e=>e));return(0,r.jsxs)("div",{className:o.codeDescContainer,children:[(0,r.jsx)("div",{className:o.desc,children:t[0]}),(0,r.jsx)("div",{className:o.example,children:t[1]})]})}},8453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>a});var i=t(6540);const o={},r=i.createContext(o);function s(e){const n=i.useContext(r);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:s(e.components),i.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[313],{4576:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>l,default:()=>p,frontMatter:()=>c,metadata:()=>d,toc:()=>h});var i=t(4848),o=t(8453),r=t(7470),s=t(1470),a=t(9365);const c={},l="Service resolution",d={id:"guides/service-resolution",title:"Service resolution",description:"When you have all your components registered and configured adequately, you can resolve them from the container or a scope by requesting their service type.",source:"@site/docs/guides/service-resolution.md",sourceDirName:"guides",slug:"/guides/service-resolution",permalink:"/stashbox/docs/guides/service-resolution",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/guides/service-resolution.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Advanced registration",permalink:"/stashbox/docs/guides/advanced-registration"},next:{title:"Lifetimes",permalink:"/stashbox/docs/guides/lifetimes"}},u={},h=[{value:"Injection patterns",id:"injection-patterns",level:2},{value:"Attributes",id:"attributes",level:2},{value:"Using your own attributes",id:"using-your-own-attributes",level:3},{value:"Dependency binding",id:"dependency-binding",level:2},{value:"Conventional resolution",id:"conventional-resolution",level:2},{value:"Conditional resolution",id:"conditional-resolution",level:2},{value:"Optional resolution",id:"optional-resolution",level:2},{value:"Dependency overrides",id:"dependency-overrides",level:2},{value:"Activation",id:"activation",level:2},{value:"Build-up",id:"build-up",level:3}];function g(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",li:"li",mdxAdmonitionTitle:"mdxAdmonitionTitle",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h1,{id:"service-resolution",children:"Service resolution"}),"\n",(0,i.jsxs)(n.p,{children:["When you have all your components registered and configured adequately, you can resolve them from the container or a ",(0,i.jsx)(n.a,{href:"/docs/guides/scopes",children:"scope"})," by requesting their ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["During a service's resolution, the container walks through the entire ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"})," and instantiates all dependencies required for the service construction.\nWhen the container encounters any violations of ",(0,i.jsx)(n.a,{href:"/docs/diagnostics/validation#resolution-validation",children:"these rules"})," ",(0,i.jsx)(n.em,{children:"(circular dependencies, missing required services, lifetime misconfigurations)"})," during the walkthrough, it lets you know that something is wrong by throwing a specific exception."]}),"\n",(0,i.jsx)(n.h2,{id:"injection-patterns",children:"Injection patterns"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Constructor injection"})," is the ",(0,i.jsx)(n.em,{children:"primary dependency injection pattern"}),". It encourages the organization of dependencies to a single place - the constructor."]}),(0,i.jsxs)(n.p,{children:["Stashbox, by default, uses the constructor that has the most parameters it knows how to resolve. This behavior is configurable through ",(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#constructor-selection",children:"constructor selection"}),"."]}),(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#property-field-injection",children:"Property/field injection"})," is also supported in cases where constructor injection is not applicable."]}),(0,i.jsxs)(n.p,{children:["Members defined with C# 11's ",(0,i.jsx)(n.a,{href:"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required",children:(0,i.jsx)(n.code,{children:"required"})})," keyword are automatically injected by the container.\nThis behavior can be controlled with ",(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#required-member-injection",children:"registration"})," or ",(0,i.jsx)(n.a,{href:"/docs/configuration/container-configuration#required-member-injection",children:"container"})," configuration options"]}),(0,i.jsxs)(n.admonition,{type:"info",children:[(0,i.jsx)(n.mdxAdmonitionTitle,{}),(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/docs/configuration/container-configuration#constructor-selection",children:"Constructor selection"})," and ",(0,i.jsx)(n.a,{href:"/docs/configuration/container-configuration#auto-member-injection",children:"property/field injection"})," is also configurable container-wide."]})]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{children:[(0,i.jsx)(a.A,{value:"Constructor injection",label:"Constructor injection",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n private readonly ILogger logger;\n private readonly IEventBroadcaster eventBroadcaster;\n\n public DbBackup(ILogger logger, IEventBroadcaster eventBroadcaster)\n {\n this.logger = logger;\n this.eventBroadcaster = eventBroadcaster;\n }\n}\n\ncontainer.Register();\ncontainer.Register();\n\ncontainer.Register();\n\n// resolution using the available constructor.\nIJob job = container.Resolve();\n"})})}),(0,i.jsx)(a.A,{value:"Property/field injection",label:"Property/field injection",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n public ILogger Logger { get; set; }\n public IEventBroadcaster EventBroadcaster { get; set; }\n\n public DbBackup() \n { }\n}\n\ncontainer.Register();\ncontainer.Register();\n\n// registration of service with auto member injection.\ncontainer.Register(options => \n options.WithAutoMemberInjection());\n\n// resolution will inject the properties.\nIJob job = container.Resolve();\n"})})})]})})]}),"\n",(0,i.jsx)(n.admonition,{type:"caution",children:(0,i.jsxs)(n.p,{children:["It's a common mistake to use the ",(0,i.jsx)(n.em,{children:"property/field injection"})," only to disencumber the constructor from having too many parameters. That's a code smell and also violates the ",(0,i.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Single-responsibility_principle",children:"Single-responsibility principle"}),". If you recognize these conditions, you should consider splitting your class into multiple smaller units rather than adding an extra property-injected dependency."]})}),"\n",(0,i.jsx)(n.h2,{id:"attributes",children:"Attributes"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(n.p,{children:"Attributes can give you control over how Stashbox selects dependencies for a service's resolution."}),(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Dependency attribute"}),":"]}),(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"On a constructor/method parameter"}),": used with the ",(0,i.jsx)(n.em,{children:"name"})," property, it works as a marker for ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"On a property/field"}),": first, it enables ",(0,i.jsx)(n.em,{children:"auto-injection"})," on the marked property/field (even if it wasn't configured at registration explicitly), and just as with the method parameter, it allows ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"}),"."]}),"\n"]}),"\n"]}),(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"DependencyName attribute"}),": a parameter marked with this attribute will get the related service's dependency name."]}),(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"InjectionMethod attribute"}),": marks a method to be called when the requested service is instantiated."]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{children:[(0,i.jsx)(a.A,{value:"Constructor",label:"Constructor",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n private readonly ILogger logger;\n\n public DbBackup([Dependency("Console")]ILogger logger)\n {\n this.logger = logger;\n }\n}\n\ncontainer.Register("Console");\ncontainer.Register("File");\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})}),(0,i.jsx)(a.A,{value:"Property/field",label:"Property/field",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n [Dependency("Console")]\n public ILogger Logger { get; set; }\n\n public DbBackup() \n { }\n}\n\ncontainer.Register("Console");\ncontainer.Register("File");\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})}),(0,i.jsx)(a.A,{value:"DependencyName",label:"DependencyName",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n public string Name { get; set; }\n\n public DbBackup([DependencyName] string name) \n { }\n}\n\ncontainer.Register("Backup");\n\n\nIJob job = container.Resolve();\n// name is "Backup".\nvar name = job.Name;\n'})})}),(0,i.jsx)(a.A,{value:"Method",label:"Method",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n [InjectionMethod]\n public void Initialize([Dependency("Console")]ILogger logger)\n {\n this.logger.Log("Initializing.");\n }\n}\n\ncontainer.Register("Console");\ncontainer.Register("File");\n\ncontainer.Register();\n\n// the container will call DbBackup\'s Initialize method.\nIJob job = container.Resolve();\n'})})})]})})]}),"\n",(0,i.jsx)(n.admonition,{type:"caution",children:(0,i.jsxs)(n.p,{children:["Attributes provide a more straightforward configuration, but using them also tightens the bond between your application and Stashbox. If you consider this an issue, you can use the ",(0,i.jsx)(n.a,{href:"/docs/guides/service-resolution#dependency-binding",children:"dependency binding"})," API or ",(0,i.jsx)(n.a,{href:"/docs/guides/service-resolution#using-your-own-attributes",children:"your own attributes"}),"."]})}),"\n",(0,i.jsx)(n.h3,{id:"using-your-own-attributes",children:"Using your own attributes"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(n.p,{children:"There's an option to extend the container's dependency finding mechanism with your own attributes."}),(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Additional Dependency attributes"}),": you can use the ",(0,i.jsx)(n.a,{href:"/docs/configuration/container-configuration#withadditionaldependencyattribute",children:(0,i.jsx)(n.code,{children:".WithAdditionalDependencyAttribute()"})})," container configuration option to let the container know that it should watch for additional attributes besides the built-in ",(0,i.jsx)(n.a,{href:"/docs/guides/service-resolution#attributes",children:(0,i.jsx)(n.code,{children:"Dependency"})})," attribute upon building up the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Additional DependencyName attributes"}),": you can use the ",(0,i.jsx)(n.a,{href:"/docs/configuration/container-configuration#withadditionaldependencynameattribute",children:(0,i.jsx)(n.code,{children:".WithAdditionalDependencyNameAttribute()"})})," container configuration option to use additional dependency name indicator attributes besides the built-in ",(0,i.jsx)(n.a,{href:"/docs/guides/service-resolution#attributes",children:(0,i.jsx)(n.code,{children:"DependencyName"})})," attribute."]}),"\n"]}),"\n"]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{children:[(0,i.jsx)(a.A,{value:"Dependency",label:"Dependency",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n [CustomDependency("Console")]\n public ILogger Logger { get; set; }\n\n public DbBackup() \n { }\n}\n\nvar container = new StashboxContainer(options => options\n .WithAdditionalDependencyAttribute());\n\ncontainer.Register("Console");\ncontainer.Register("File");\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})}),(0,i.jsx)(a.A,{value:"DependencyName",label:"DependencyName",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n public string Name { get; set; }\n\n public DbBackup([CustomName] string name) \n { }\n}\n\nvar container = new StashboxContainer(options => options\n .WithAdditionalDependencyNameAttribute());\n\ncontainer.Register("Backup");\n\nIJob job = container.Resolve();\n// name is "Backup".\nvar name = job.Name;\n'})})})]})})]}),"\n",(0,i.jsx)(n.h2,{id:"dependency-binding",children:"Dependency binding"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(n.p,{children:"The same dependency configuration functionality as attributes, but without attributes."}),(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Binding to a parameter"}),": the same functionality as the ",(0,i.jsx)(n.a,{href:"/docs/guides/service-resolution#attributes",children:(0,i.jsx)(n.code,{children:"Dependency"})})," attribute on a constructor or method parameter, enabling ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Binding to a property/field"}),": the same functionality as the ",(0,i.jsx)(n.a,{href:"/docs/guides/service-resolution#attributes",children:(0,i.jsx)(n.code,{children:"Dependency"})})," attribute, enabling the injection of the given property/field."]}),"\n"]}),"\n"]}),(0,i.jsx)(n.admonition,{type:"info",children:(0,i.jsxs)(n.p,{children:["There are further dependency binding options ",(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#dependency-configuration",children:"available"})," on the registration configuration API."]})})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{children:[(0,i.jsx)(a.A,{value:"Bind to parameter",label:"Bind to parameter",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n public DbBackup(ILogger logger)\n { }\n}\n\ncontainer.Register("Console");\ncontainer.Register("File");\n\n// registration of service with the dependency binding.\ncontainer.Register(options => options\n .WithDependencyBinding("logger", "Console"));\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})}),(0,i.jsx)(a.A,{value:"Bind to property / field",label:"Bind to property / field",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n public ILogger Logger { get; set; }\n}\n\ncontainer.Register("Console");\ncontainer.Register("File");\n\n// registration of service with the member injection.\ncontainer.Register(options => options\n .WithDependencyBinding("Logger", "Console"));\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})})]})})]}),"\n",(0,i.jsx)(n.h2,{id:"conventional-resolution",children:"Conventional resolution"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(n.p,{children:"When you enable conventional resolution, the container treats member and method parameter names as their dependency identifier."}),(0,i.jsx)(n.p,{children:"It's like an implicit dependency binding on every class member."}),(0,i.jsx)(n.p,{children:"First, you have to enable conventional resolution through the configuration of the container:"}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .TreatParameterAndMemberNameAsDependencyName());\n"})}),(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["The container will attempt a ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"})," on each dependency based on their parameter or property/field name."]})})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{children:[(0,i.jsx)(a.A,{value:"Parameters",label:"Parameters",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n public DbBackup(\n // the parameter name identifies the dependency.\n ILogger consoleLogger)\n { }\n}\n\ncontainer.Register("consoleLogger");\ncontainer.Register("fileLogger");\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})}),(0,i.jsx)(a.A,{value:"Properties / fields",label:"Properties / fields",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n // the property name identifies the dependency.\n public ILogger ConsoleLogger { get; set; }\n}\n\ncontainer.Register("ConsoleLogger");\ncontainer.Register("FileLogger");\n\n// registration of service with auto member injection.\ncontainer.Register(options => options\n .WithAutoMemberInjection());\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n'})})})]})})]}),"\n",(0,i.jsx)(n.h2,{id:"conditional-resolution",children:"Conditional resolution"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(n.p,{children:"Stashbox can resolve a particular dependency based on its context. This context is typically the reflected type of dependency, its usage, and the type it gets injected into."}),(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Attribute"}),": you can filter on constructor, method, property, or field attributes to select the desired dependency for your service. In contrast to the ",(0,i.jsx)(n.code,{children:"Dependency"})," attribute, this configuration doesn't tie your application to Stashbox because you use your own attributes."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Parent type"}),": you can filter on what type the given service is injected into."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Resolution path"}),": similar to the parent type and attribute condition but extended with inheritance. You can set that the given service is only usable in a type's resolution path. This means that each direct and sub-dependency of the selected type must use the provided service as a dependency."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Custom"}),": with this, you can build your own selection logic based on the given contextual type information."]}),"\n"]}),"\n"]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{children:[(0,i.jsx)(a.A,{value:"Attribute",label:"Attribute",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class ConsoleAttribute : Attribute { }\n\nclass DbBackup : IJob\n{\n public DbBackup([Console]ILogger logger)\n { }\n}\n\ncontainer.Register(options => options\n // resolve only when the injected parameter, \n // property or field has the 'Console' attribute\n .WhenHas());\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n"})})}),(0,i.jsx)(a.A,{value:"Parent",label:"Parent",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n public DbBackup(ILogger logger)\n { }\n}\n\ncontainer.Register(options => options\n // inject only when we are \n // currently resolving DbBackup OR StorageCleanup.\n .WhenDependantIs()\n .WhenDependantIs());\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n"})})}),(0,i.jsx)(a.A,{value:"Path",label:"Path",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n public DbBackup(IStorage storage)\n { }\n}\n\nclass FileStorage : IStorage\n{\n public FileStorage(ILogger logger) \n { }\n}\n\ncontainer.Register(options => options\n // inject only when we are in the\n // resolution path of DbBackup\n .WhenInResolutionPathOf());\n\ncontainer.Register();\ncontainer.Register();\n\n// the container will select ConsoleLogger for FileStorage\n// because they are injected into DbBackup.\nIJob job = container.Resolve();\n"})})}),(0,i.jsx)(a.A,{value:"Custom",label:"Custom",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n public DbBackup(ILogger logger)\n { }\n}\n\ncontainer.Register(options => options\n // inject only when we are \n // currently resolving DbBackup.\n .When(typeInfo => typeInfo.ParentType.Equals(typeof(DbBackup))));\n\ncontainer.Register();\n\n// the container will resolve DbBackup with ConsoleLogger.\nIJob job = container.Resolve();\n"})})}),(0,i.jsx)(a.A,{value:"Collection",label:"Collection",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbJobsExecutor : IJobsExecutor\n{\n public DbBackup(IEnumerable jobs)\n { }\n}\n\ncontainer.Register(options => options\n .WhenDependantIs());\ncontainer.Register(options => options\n .WhenDependantIs());\nontainer.Register();\n\ncontainer.Register();\n\n// jobsExecutor will get DbBackup and DbCleanup within a collection.\nIJobsExecutor jobsExecutor = container.Resolve();\n"})})})]})})]}),"\n",(0,i.jsxs)(n.p,{children:["The specified conditions are behaving like filters when a ",(0,i.jsx)(n.strong,{children:"collection"})," is requested."]}),"\n",(0,i.jsxs)(n.p,{children:["When you use the same conditional option multiple times, the container will evaluate them ",(0,i.jsx)(n.strong,{children:"with OR"})," logical operator."]}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#conditions",children:"Here"})," you can find each condition related registration option."]})}),"\n",(0,i.jsx)(n.h2,{id:"optional-resolution",children:"Optional resolution"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(n.p,{children:["In cases where it's not guaranteed that a service is resolvable, either because it's not registered or any of its dependencies are missing, you can attempt an optional resolution using the ",(0,i.jsx)(n.code,{children:"ResolveOrDefault()"})," method."]}),(0,i.jsxs)(n.p,{children:["When the resolution attempt fails, it will return ",(0,i.jsx)(n.code,{children:"null"})," (or ",(0,i.jsx)(n.code,{children:"default"})," in case of value types)."]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{groupId:"generic-runtime-apis",children:[(0,i.jsx)(a.A,{value:"Generic API",label:"Generic API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"// returns null when the resolution fails.\nIJob job = container.ResolveOrDefault();\n\n// throws ResolutionFailedException when the resolution fails.\nIJob job = container.Resolve();\n"})})}),(0,i.jsx)(a.A,{value:"Runtime type API",label:"Runtime type API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"// returns null when the resolution fails.\nobject job = container.ResolveOrDefault(typeof(IJob));\n\n// throws ResolutionFailedException when the resolution fails.\nobject job = container.Resolve(typeof(IJob));\n"})})})]})})]}),"\n",(0,i.jsx)(n.h2,{id:"dependency-overrides",children:"Dependency overrides"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(n.p,{children:["At resolution time, you can override a service's dependencies by passing an ",(0,i.jsx)(n.code,{children:"object[]"})," to the ",(0,i.jsx)(n.code,{children:"Resolve()"})," method."]}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n public DbBackup(ILogger logger)\n { }\n}\n"})})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{groupId:"generic-runtime-apis",children:[(0,i.jsx)(a.A,{value:"Generic API",label:"Generic API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"DbBackup backup = container.Resolve( \n dependencyOverrides: new object[] \n { \n new ConsoleLogger() \n });\n"})})}),(0,i.jsx)(a.A,{value:"Runtime type API",label:"Runtime type API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"object backup = container.Resolve(typeof(DbBackup),\n dependencyOverrides: new object[] \n { \n new ConsoleLogger() \n });\n"})})})]})})]}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(n.p,{children:["To get more control over your overrides (like giving them name to allow ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"}),"), you can use the ",(0,i.jsx)(n.code,{children:"Override"})," built-in wrapper."]}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'class DbBackup : IJob\n{\n public DbBackup([Dependency("console")]ILogger logger)\n { }\n}\n'})})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{groupId:"generic-runtime-apis",children:[(0,i.jsx)(a.A,{value:"Generic API",label:"Generic API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'DbBackup backup = container.Resolve( \n dependencyOverrides: new object[] \n { \n Override.Of(new ConsoleLogger(), "console"), \n });\n'})})}),(0,i.jsx)(a.A,{value:"Runtime type API",label:"Runtime type API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:'object backup = container.Resolve(typeof(DbBackup),\n dependencyOverrides: new object[] \n { \n Override.Of(typeof(ILogger), new ConsoleLogger(), "console"), \n });\n'})})})]})})]}),"\n",(0,i.jsx)(n.h2,{id:"activation",children:"Activation"}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(n.p,{children:["You can use the container's ",(0,i.jsx)(n.code,{children:".Activate()"})," method when you only want to build up an instance from a type on the fly without registration."]}),(0,i.jsxs)(n.p,{children:["It allows dependency overriding with ",(0,i.jsx)(n.code,{children:"object"})," arguments and performs property/field/method injection (when configured)."]}),(0,i.jsxs)(n.p,{children:["It works like ",(0,i.jsx)(n.code,{children:"Activator.CreateInstance()"})," except that Stashbox supplies the dependencies."]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(s.A,{groupId:"generic-runtime-apis",children:[(0,i.jsx)(a.A,{value:"Generic API",label:"Generic API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"// use dependency injected by container.\nDbBackup backup = container.Activate();\n\n// override the injected dependency.\nDbBackup backup = container.Activate(new ConsoleLogger());\n"})})}),(0,i.jsx)(a.A,{value:"Runtime type API",label:"Runtime type API",children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"// use dependency injected by container.\nobject backup = container.Activate(typeof(DbBackup));\n\n// override the injected dependency.\nobject backup = container.Activate(typeof(DbBackup), new ConsoleLogger());\n"})})})]})})]}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(n.h3,{id:"build-up",children:"Build-up"}),(0,i.jsxs)(n.p,{children:["With the ",(0,i.jsx)(n.code,{children:".BuildUp()"})," method, you can do the same ",(0,i.jsx)(n.em,{children:"on the fly"})," post-processing (property/field/method injection) on already constructed instances."]}),(0,i.jsx)(n.admonition,{type:"caution",children:(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:".BuildUp()"})," won't register the given instance into the container."]})})]}),(0,i.jsx)("div",{children:(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob\n{\n public ILogger Logger { get; set; }\n}\n\nDbBackup backup = new DbBackup();\n// the container fills the Logger property.\ncontainer.BuildUp(backup); \n"})})})]})]})}function p(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(g,{...e})}):g(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>s});t(6540);var i=t(870);const o={tabItem:"tabItem_Ymn6"};var r=t(4848);function s(e){let{children:n,hidden:t,className:s}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,i.A)(o.tabItem,s),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>I});var i=t(6540),o=t(870),r=t(3104),s=t(6347),a=t(205),c=t(7485),l=t(1682),d=t(9466);function u(e){return i.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,i.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:t}=e;return(0,i.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:t,attributes:i,default:o}}=e;return{value:n,label:t,attributes:i,default:o}}))}(t);return function(e){const n=(0,l.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function g(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:t}=e;const o=(0,s.W6)(),r=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,c.aZ)(r),(0,i.useCallback)((e=>{if(!r)return;const n=new URLSearchParams(o.location.search);n.set(r,e),o.replace({...o.location,search:n.toString()})}),[r,o])]}function b(e){const{defaultValue:n,queryString:t=!1,groupId:o}=e,r=h(e),[s,c]=(0,i.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!g({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const i=t.find((e=>e.default))??t[0];if(!i)throw new Error("Unexpected error: 0 tabValues");return i.value}({defaultValue:n,tabValues:r}))),[l,u]=p({queryString:t,groupId:o}),[b,j]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[o,r]=(0,d.Dv)(t);return[o,(0,i.useCallback)((e=>{t&&r.set(e)}),[t,r])]}({groupId:o}),x=(()=>{const e=l??b;return g({value:e,tabValues:r})?e:null})();(0,a.A)((()=>{x&&c(x)}),[x]);return{selectedValue:s,selectValue:(0,i.useCallback)((e=>{if(!g({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);c(e),u(e),j(e)}),[u,j,r]),tabValues:r}}var j=t(2303);const x={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var m=t(4848);function v(e){let{className:n,block:t,selectedValue:i,selectValue:s,tabValues:a}=e;const c=[],{blockElementScrollPositionUntilNextRender:l}=(0,r.a_)(),d=e=>{const n=e.currentTarget,t=c.indexOf(n),o=a[t].value;o!==i&&(l(n),s(o))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const t=c.indexOf(e.currentTarget)+1;n=c[t]??c[0];break}case"ArrowLeft":{const t=c.indexOf(e.currentTarget)-1;n=c[t]??c[c.length-1];break}}n?.focus()};return(0,m.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,o.A)("tabs",{"tabs--block":t},n),children:a.map((e=>{let{value:n,label:t,attributes:r}=e;return(0,m.jsx)("li",{role:"tab",tabIndex:i===n?0:-1,"aria-selected":i===n,ref:e=>c.push(e),onKeyDown:u,onClick:d,...r,className:(0,o.A)("tabs__item",x.tabItem,r?.className,{"tabs__item--active":i===n}),children:t??n},n)}))})}function y(e){let{lazy:n,children:t,selectedValue:o}=e;const r=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=r.find((e=>e.props.value===o));return e?(0,i.cloneElement)(e,{className:"margin-top--md"}):null}return(0,m.jsx)("div",{className:"margin-top--md",children:r.map(((e,n)=>(0,i.cloneElement)(e,{key:n,hidden:e.props.value!==o})))})}function f(e){const n=b(e);return(0,m.jsxs)("div",{className:(0,o.A)("tabs-container",x.tabList),children:[(0,m.jsx)(v,{...e,...n}),(0,m.jsx)(y,{...e,...n})]})}function I(e){const n=(0,j.A)();return(0,m.jsx)(f,{...e,children:u(e.children)},String(n))}},7470:(e,n,t)=>{t.d(n,{A:()=>s});var i=t(6540);const o={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var r=t(4848);function s(e){let{children:n}=e,t=i.Children.toArray(n).filter((e=>e));return(0,r.jsxs)("div",{className:o.codeDescContainer,children:[(0,r.jsx)("div",{className:o.desc,children:t[0]}),(0,r.jsx)("div",{className:o.example,children:t[1]})]})}},8453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>a});var i=t(6540);const o={},r=i.createContext(o);function s(e){const n=i.useContext(r);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:s(e.components),i.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/115c3c33.0d0b2113.js b/assets/js/115c3c33.02b13161.js similarity index 97% rename from assets/js/115c3c33.0d0b2113.js rename to assets/js/115c3c33.02b13161.js index f5ba181f..6b89e91f 100644 --- a/assets/js/115c3c33.0d0b2113.js +++ b/assets/js/115c3c33.02b13161.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[793],{1830:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>c,default:()=>p,frontMatter:()=>a,metadata:()=>l,toc:()=>u});var i=t(4848),s=t(8453),r=t(1470),o=t(9365);const a={},c="Validation",l={id:"diagnostics/validation",title:"Validation",description:"Stashbox validation routines help you detect and solve common misconfiguration issues. You can verify the container's actual state with its .Validate() method. It walks through the whole resolution tree and collects all issues into an AggregateException.",source:"@site/docs/diagnostics/validation.md",sourceDirName:"diagnostics",slug:"/diagnostics/validation",permalink:"/stashbox/docs/diagnostics/validation",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/diagnostics/validation.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Special resolution cases",permalink:"/stashbox/docs/advanced/special-resolution-cases"},next:{title:"Utilities",permalink:"/stashbox/docs/diagnostics/utilities"}},d={},u=[{value:"Registration validation",id:"registration-validation",level:2},{value:"InvalidRegistrationException",id:"invalidregistrationexception",level:3},{value:"ServiceAlreadyRegisteredException",id:"servicealreadyregisteredexception",level:3},{value:"Resolution validation",id:"resolution-validation",level:2},{value:"Lifetime validation",id:"lifetime-validation",level:2},{value:"Circular dependency",id:"circular-dependency",level:2},{value:"Other exceptions",id:"other-exceptions",level:2},{value:"CompositionRootNotFoundException",id:"compositionrootnotfoundexception",level:3},{value:"ConstructorNotFoundException",id:"constructornotfoundexception",level:3}];function h(e){const n={a:"a",br:"br",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h1,{id:"validation",children:"Validation"}),"\n",(0,i.jsxs)(n.p,{children:["Stashbox validation routines help you detect and solve common misconfiguration issues. You can verify the container's actual state with its ",(0,i.jsx)(n.code,{children:".Validate()"})," method. It walks through the whole ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"})," and collects all issues into an ",(0,i.jsx)(n.code,{children:"AggregateException"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"registration-validation",children:"Registration validation"}),"\n",(0,i.jsx)(n.p,{children:"During registration, the container validates the passed types and throws the following exceptions when the validation fails."}),"\n",(0,i.jsx)(n.h3,{id:"invalidregistrationexception",children:"InvalidRegistrationException"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsxs)(n.strong,{children:["When the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," is not resolvable."]})," (it's an interface or an abstract class registered like: ",(0,i.jsx)(n.code,{children:"Register()"}),"):"]}),"\n"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"The type Namespace.IService could not be resolved. It's probably an interface, abstract class, or primitive type.\n"})}),"\n",(0,i.jsxs)(n.ol,{start:"2",children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsxs)(n.strong,{children:["When the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," does not implement the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})]}),"."]}),"\n"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"The type Namespace.MotorCycle does not implement the '[service type](/docs/getting-started/glossary#service-type--implementation-type)' Namespace.ICar.\n"})}),"\n",(0,i.jsx)(n.h3,{id:"servicealreadyregisteredexception",children:"ServiceAlreadyRegisteredException"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsxs)(n.strong,{children:["When the given ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," is already registered"]})," and the ",(0,i.jsx)(n.code,{children:"RegistrationBehavior"})," ",(0,i.jsx)(n.a,{href:"/docs/configuration/container-configuration#registration-behavior",children:"container configuration option"})," is set to ",(0,i.jsx)(n.code,{children:"ThrowException"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"The type Namespace.Service is already registered.\n"})}),"\n",(0,i.jsx)(n.h2,{id:"resolution-validation",children:"Resolution validation"}),"\n",(0,i.jsxs)(n.p,{children:["During the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree's"})," construction, the container continuously checks its actual state to ensure stability. When any of the following issues occur, the container throws a ",(0,i.jsx)(n.code,{children:"ResolutionFailedException"}),"."]}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsxs)(n.strong,{children:["When a dependency is missing from the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"})]}),"."]}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)(o.A,{value:"Parameter",label:"Parameter",children:[(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class Service\n{\n public Service(Dependency dep) { }\n\n public Service(Dependency2 dep2) { }\n}\n\ncontainer.Register();\nvar service = container.Resolve();\n"})}),(0,i.jsx)(n.p,{children:"This will result in the following exception message:"}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Could not resolve type Namespace.Service.\nConstructor Void .ctor(Dependency) found with unresolvable parameter: (Namespace.Dependency)dep.\nConstructor Void .ctor(Dependency2) found with unresolvable parameter: (Namespace.Dependency2)dep2.\n"})})]}),(0,i.jsxs)(o.A,{value:"Property / field",label:"Property / field",children:[(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class Service\n{\n public Dependency Dep { get; set; }\n}\n\ncontainer.Register(options => options.WithDependencyBinding(s => s.Dep));\nvar service = container.Resolve();\n"})}),(0,i.jsx)(n.p,{children:"This will show the following message:"}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Could not resolve type Namespace.Service.\nUnresolvable property: (Namespace.Dependency)Dep.\n"})})]})]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"When the requested type is unresolvable."})," E.g., it doesn't have a public constructor."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Could not resolve type Namespace.Service.\nService is not registered or unresolvable type requested.\n"})}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"lifetime-validation",children:"Lifetime validation"}),"\n",(0,i.jsxs)(n.p,{children:["This validation enforces the following rules. When they are violated, the container throws a ",(0,i.jsx)(n.code,{children:"LifetimeValidationFailedException"}),"."]}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsxs)(n.strong,{children:["When a scoped service is requested from the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#root-scope",children:"root scope"})]}),".",(0,i.jsx)(n.br,{}),"\n","As the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#root-scope",children:"root scope's"})," lifetime is bound to the container's lifetime, this action unintentionally promotes the scoped service's lifetime to singleton:"]}),"\n"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Resolution of Namespace.Service (ScopedLifetime) from the '[root scope](/docs/getting-started/glossary#root-scope)' is not allowed, \nthat would promote the service's lifetime to a singleton.\n"})}),"\n",(0,i.jsxs)(n.ol,{start:"2",children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"When the life-span of a dependency is shorter than its parent's"}),".",(0,i.jsx)(n.br,{}),"\n","It's called ",(0,i.jsx)(n.a,{href:"https://blog.ploeh.dk/2014/06/02/captive-dependency/",children:"captive dependency"}),". Every lifetime has a ",(0,i.jsx)(n.code,{children:"LifeSpan"})," value, which determines how long the related service lives. The main rule is that services may not contain dependencies with shorter life spans. E.g., singletons should not depend on scoped services. The only exception is the life span value ",(0,i.jsx)(n.code,{children:"0"}),", which indicates that the related service is state-less and could be injected into any service."]}),"\n",(0,i.jsxs)(n.p,{children:["These are the ",(0,i.jsx)(n.code,{children:"LifeSpan"})," values of the pre-defined lifetimes:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Singleton"}),": 20"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Scoped"}),": 10"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"NamedScope"}),": 10"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"PerRequest"}),": 0"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"PerScopedRequest"}),": 0"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Transient"}),": 0"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"In case of a failed validation the exception message would be:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"The life-span of Namespace.Service (ScopedLifetime|10) is shorter than \nits direct or indirect parent's Namespace.Dependency (Singleton|20). \nThis could lead to incidental lifetime promotions with longer life-span, \nit's recommended to double-check your lifetime configurations.\n"})}),"\n",(0,i.jsx)(n.h2,{id:"circular-dependency",children:"Circular dependency"}),"\n",(0,i.jsxs)(n.p,{children:["When the container encounters a circular dependency loop in the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"}),", it throws a ",(0,i.jsx)(n.code,{children:"CircularDependencyException"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class Service1\n{\n public Service1(Service2 service2) { }\n}\n\nclass Service2\n{\n public Service2(Service1 service1) { }\n}\n\ncontainer.Register();\ncontainer.Register();\nvar service = container.Resolve();\n"})}),"\n",(0,i.jsx)(n.p,{children:"The exception message is:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Circular dependency detected during the resolution of Namespace.Service1.\n"})}),"\n",(0,i.jsx)(n.h2,{id:"other-exceptions",children:"Other exceptions"}),"\n",(0,i.jsx)(n.h3,{id:"compositionrootnotfoundexception",children:"CompositionRootNotFoundException"}),"\n",(0,i.jsxs)(n.p,{children:["This exception pops up when we try to compose an assembly, but it doesn't contain an ",(0,i.jsx)(n.code,{children:"ICompositionRoot"})," implementation."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"container.ComposeAssembly(typeof(Service).Assembly);\n"})}),"\n",(0,i.jsx)(n.p,{children:"The exception message is:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"No ICompositionRoot found in the given assembly: {your-assembly-name}\n"})}),"\n",(0,i.jsx)(n.h3,{id:"constructornotfoundexception",children:"ConstructorNotFoundException"}),"\n",(0,i.jsxs)(n.p,{children:["During the registration phase, when you are using the ",(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#withconstructorbyargumenttypes",children:(0,i.jsx)(n.code,{children:"WithConstructorByArgumentTypes()"})})," or ",(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#withconstructorbyarguments",children:(0,i.jsx)(n.code,{children:"WithConstructorByArguments()"})})," options, you can accidentally point to a non-existing constructor. In that case, the container throws a ",(0,i.jsx)(n.code,{children:"ConstructorNotFoundException"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class Service\n{\n public Service(Dependency dep) { }\n}\n\ncontainer.Register(options => options.WithConstructorByArgumentTypes(typeof(string), typeof(int)));\n"})}),"\n",(0,i.jsx)(n.p,{children:"The exception message is:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Constructor not found for Namespace.Service with the given argument types: System.String, System.Int32.\n"})})]})}function p(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>o});t(6540);var i=t(870);const s={tabItem:"tabItem_Ymn6"};var r=t(4848);function o(e){let{children:n,hidden:t,className:o}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,i.A)(s.tabItem,o),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>w});var i=t(6540),s=t(870),r=t(3104),o=t(6347),a=t(205),c=t(7485),l=t(1682),d=t(9466);function u(e){return i.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,i.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:t}=e;return(0,i.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:t,attributes:i,default:s}}=e;return{value:n,label:t,attributes:i,default:s}}))}(t);return function(e){const n=(0,l.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function p(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function x(e){let{queryString:n=!1,groupId:t}=e;const s=(0,o.W6)(),r=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,c.aZ)(r),(0,i.useCallback)((e=>{if(!r)return;const n=new URLSearchParams(s.location.search);n.set(r,e),s.replace({...s.location,search:n.toString()})}),[r,s])]}function v(e){const{defaultValue:n,queryString:t=!1,groupId:s}=e,r=h(e),[o,c]=(0,i.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const i=t.find((e=>e.default))??t[0];if(!i)throw new Error("Unexpected error: 0 tabValues");return i.value}({defaultValue:n,tabValues:r}))),[l,u]=x({queryString:t,groupId:s}),[v,g]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[s,r]=(0,d.Dv)(t);return[s,(0,i.useCallback)((e=>{t&&r.set(e)}),[t,r])]}({groupId:s}),m=(()=>{const e=l??v;return p({value:e,tabValues:r})?e:null})();(0,a.A)((()=>{m&&c(m)}),[m]);return{selectedValue:o,selectValue:(0,i.useCallback)((e=>{if(!p({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);c(e),u(e),g(e)}),[u,g,r]),tabValues:r}}var g=t(2303);const m={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var f=t(4848);function j(e){let{className:n,block:t,selectedValue:i,selectValue:o,tabValues:a}=e;const c=[],{blockElementScrollPositionUntilNextRender:l}=(0,r.a_)(),d=e=>{const n=e.currentTarget,t=c.indexOf(n),s=a[t].value;s!==i&&(l(n),o(s))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const t=c.indexOf(e.currentTarget)+1;n=c[t]??c[0];break}case"ArrowLeft":{const t=c.indexOf(e.currentTarget)-1;n=c[t]??c[c.length-1];break}}n?.focus()};return(0,f.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":t},n),children:a.map((e=>{let{value:n,label:t,attributes:r}=e;return(0,f.jsx)("li",{role:"tab",tabIndex:i===n?0:-1,"aria-selected":i===n,ref:e=>c.push(e),onKeyDown:u,onClick:d,...r,className:(0,s.A)("tabs__item",m.tabItem,r?.className,{"tabs__item--active":i===n}),children:t??n},n)}))})}function y(e){let{lazy:n,children:t,selectedValue:s}=e;const r=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=r.find((e=>e.props.value===s));return e?(0,i.cloneElement)(e,{className:"margin-top--md"}):null}return(0,f.jsx)("div",{className:"margin-top--md",children:r.map(((e,n)=>(0,i.cloneElement)(e,{key:n,hidden:e.props.value!==s})))})}function b(e){const n=v(e);return(0,f.jsxs)("div",{className:(0,s.A)("tabs-container",m.tabList),children:[(0,f.jsx)(j,{...e,...n}),(0,f.jsx)(y,{...e,...n})]})}function w(e){const n=(0,g.A)();return(0,f.jsx)(b,{...e,children:u(e.children)},String(n))}},8453:(e,n,t)=>{t.d(n,{R:()=>o,x:()=>a});var i=t(6540);const s={},r=i.createContext(s);function o(e){const n=i.useContext(r);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),i.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[793],{1830:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>c,default:()=>p,frontMatter:()=>a,metadata:()=>l,toc:()=>u});var i=t(4848),s=t(8453),r=t(1470),o=t(9365);const a={},c="Validation",l={id:"diagnostics/validation",title:"Validation",description:"Stashbox validation routines help you detect and solve common misconfiguration issues. You can verify the container's actual state with its .Validate() method. It walks through the whole resolution tree and collects all issues into an AggregateException.",source:"@site/docs/diagnostics/validation.md",sourceDirName:"diagnostics",slug:"/diagnostics/validation",permalink:"/stashbox/docs/diagnostics/validation",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/diagnostics/validation.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Special resolution cases",permalink:"/stashbox/docs/advanced/special-resolution-cases"},next:{title:"Utilities",permalink:"/stashbox/docs/diagnostics/utilities"}},d={},u=[{value:"Registration validation",id:"registration-validation",level:2},{value:"InvalidRegistrationException",id:"invalidregistrationexception",level:3},{value:"ServiceAlreadyRegisteredException",id:"servicealreadyregisteredexception",level:3},{value:"Resolution validation",id:"resolution-validation",level:2},{value:"Lifetime validation",id:"lifetime-validation",level:2},{value:"Circular dependency",id:"circular-dependency",level:2},{value:"Other exceptions",id:"other-exceptions",level:2},{value:"CompositionRootNotFoundException",id:"compositionrootnotfoundexception",level:3},{value:"ConstructorNotFoundException",id:"constructornotfoundexception",level:3}];function h(e){const n={a:"a",br:"br",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h1,{id:"validation",children:"Validation"}),"\n",(0,i.jsxs)(n.p,{children:["Stashbox validation routines help you detect and solve common misconfiguration issues. You can verify the container's actual state with its ",(0,i.jsx)(n.code,{children:".Validate()"})," method. It walks through the whole ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"})," and collects all issues into an ",(0,i.jsx)(n.code,{children:"AggregateException"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"registration-validation",children:"Registration validation"}),"\n",(0,i.jsx)(n.p,{children:"During registration, the container validates the passed types and throws the following exceptions when the validation fails."}),"\n",(0,i.jsx)(n.h3,{id:"invalidregistrationexception",children:"InvalidRegistrationException"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsxs)(n.strong,{children:["When the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," is not resolvable."]})," (it's an interface or an abstract class registered like: ",(0,i.jsx)(n.code,{children:"Register()"}),"):"]}),"\n"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"The type Namespace.IService could not be resolved. It's probably an interface, abstract class, or primitive type.\n"})}),"\n",(0,i.jsxs)(n.ol,{start:"2",children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsxs)(n.strong,{children:["When the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," does not implement the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})]}),"."]}),"\n"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"The type Namespace.MotorCycle does not implement the '[service type](/docs/getting-started/glossary#service-type--implementation-type)' Namespace.ICar.\n"})}),"\n",(0,i.jsx)(n.h3,{id:"servicealreadyregisteredexception",children:"ServiceAlreadyRegisteredException"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsxs)(n.strong,{children:["When the given ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," is already registered"]})," and the ",(0,i.jsx)(n.code,{children:"RegistrationBehavior"})," ",(0,i.jsx)(n.a,{href:"/docs/configuration/container-configuration#registration-behavior",children:"container configuration option"})," is set to ",(0,i.jsx)(n.code,{children:"ThrowException"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"The type Namespace.Service is already registered.\n"})}),"\n",(0,i.jsx)(n.h2,{id:"resolution-validation",children:"Resolution validation"}),"\n",(0,i.jsxs)(n.p,{children:["During the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree's"})," construction, the container continuously checks its actual state to ensure stability. When any of the following issues occur, the container throws a ",(0,i.jsx)(n.code,{children:"ResolutionFailedException"}),"."]}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsxs)(n.strong,{children:["When a dependency is missing from the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"})]}),"."]}),"\n",(0,i.jsxs)(r.A,{children:[(0,i.jsxs)(o.A,{value:"Parameter",label:"Parameter",children:[(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class Service\n{\n public Service(Dependency dep) { }\n\n public Service(Dependency2 dep2) { }\n}\n\ncontainer.Register();\nvar service = container.Resolve();\n"})}),(0,i.jsx)(n.p,{children:"This will result in the following exception message:"}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Could not resolve type Namespace.Service.\nConstructor Void .ctor(Dependency) found with unresolvable parameter: (Namespace.Dependency)dep.\nConstructor Void .ctor(Dependency2) found with unresolvable parameter: (Namespace.Dependency2)dep2.\n"})})]}),(0,i.jsxs)(o.A,{value:"Property / field",label:"Property / field",children:[(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class Service\n{\n public Dependency Dep { get; set; }\n}\n\ncontainer.Register(options => options.WithDependencyBinding(s => s.Dep));\nvar service = container.Resolve();\n"})}),(0,i.jsx)(n.p,{children:"This will show the following message:"}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Could not resolve type Namespace.Service.\nUnresolvable property: (Namespace.Dependency)Dep.\n"})})]})]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"When the requested type is unresolvable."})," E.g., it doesn't have a public constructor."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Could not resolve type Namespace.Service.\nService is not registered or unresolvable type requested.\n"})}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"lifetime-validation",children:"Lifetime validation"}),"\n",(0,i.jsxs)(n.p,{children:["This validation enforces the following rules. When they are violated, the container throws a ",(0,i.jsx)(n.code,{children:"LifetimeValidationFailedException"}),"."]}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsxs)(n.strong,{children:["When a scoped service is requested from the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#root-scope",children:"root scope"})]}),".",(0,i.jsx)(n.br,{}),"\n","As the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#root-scope",children:"root scope's"})," lifetime is bound to the container's lifetime, this action unintentionally promotes the scoped service's lifetime to singleton:"]}),"\n"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Resolution of Namespace.Service (ScopedLifetime) from the '[root scope](/docs/getting-started/glossary#root-scope)' is not allowed, \nthat would promote the service's lifetime to a singleton.\n"})}),"\n",(0,i.jsxs)(n.ol,{start:"2",children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"When the life-span of a dependency is shorter than its parent's"}),".",(0,i.jsx)(n.br,{}),"\n","It's called ",(0,i.jsx)(n.a,{href:"https://blog.ploeh.dk/2014/06/02/captive-dependency/",children:"captive dependency"}),". Every lifetime has a ",(0,i.jsx)(n.code,{children:"LifeSpan"})," value, which determines how long the related service lives. The main rule is that services may not contain dependencies with shorter life spans. E.g., singletons should not depend on scoped services. The only exception is the life span value ",(0,i.jsx)(n.code,{children:"0"}),", which indicates that the related service is state-less and could be injected into any service."]}),"\n",(0,i.jsxs)(n.p,{children:["These are the ",(0,i.jsx)(n.code,{children:"LifeSpan"})," values of the pre-defined lifetimes:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Singleton"}),": 20"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Scoped"}),": 10"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"NamedScope"}),": 10"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"PerRequest"}),": 0"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"PerScopedRequest"}),": 0"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Transient"}),": 0"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"In case of a failed validation the exception message would be:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"The life-span of Namespace.Service (ScopedLifetime|10) is shorter than \nits direct or indirect parent's Namespace.Dependency (Singleton|20). \nThis could lead to incidental lifetime promotions with longer life-span, \nit's recommended to double-check your lifetime configurations.\n"})}),"\n",(0,i.jsx)(n.h2,{id:"circular-dependency",children:"Circular dependency"}),"\n",(0,i.jsxs)(n.p,{children:["When the container encounters a circular dependency loop in the ",(0,i.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"}),", it throws a ",(0,i.jsx)(n.code,{children:"CircularDependencyException"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class Service1\n{\n public Service1(Service2 service2) { }\n}\n\nclass Service2\n{\n public Service2(Service1 service1) { }\n}\n\ncontainer.Register();\ncontainer.Register();\nvar service = container.Resolve();\n"})}),"\n",(0,i.jsx)(n.p,{children:"The exception message is:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Circular dependency detected during the resolution of Namespace.Service1.\n"})}),"\n",(0,i.jsx)(n.h2,{id:"other-exceptions",children:"Other exceptions"}),"\n",(0,i.jsx)(n.h3,{id:"compositionrootnotfoundexception",children:"CompositionRootNotFoundException"}),"\n",(0,i.jsxs)(n.p,{children:["This exception pops up when we try to compose an assembly, but it doesn't contain an ",(0,i.jsx)(n.code,{children:"ICompositionRoot"})," implementation."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"container.ComposeAssembly(typeof(Service).Assembly);\n"})}),"\n",(0,i.jsx)(n.p,{children:"The exception message is:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"No ICompositionRoot found in the given assembly: {your-assembly-name}\n"})}),"\n",(0,i.jsx)(n.h3,{id:"constructornotfoundexception",children:"ConstructorNotFoundException"}),"\n",(0,i.jsxs)(n.p,{children:["During the registration phase, when you are using the ",(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#withconstructorbyargumenttypes",children:(0,i.jsx)(n.code,{children:"WithConstructorByArgumentTypes()"})})," or ",(0,i.jsx)(n.a,{href:"/docs/configuration/registration-configuration#withconstructorbyarguments",children:(0,i.jsx)(n.code,{children:"WithConstructorByArguments()"})})," options, you can accidentally point to a non-existing constructor. In that case, the container throws a ",(0,i.jsx)(n.code,{children:"ConstructorNotFoundException"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cs",children:"class Service\n{\n public Service(Dependency dep) { }\n}\n\ncontainer.Register(options => options.WithConstructorByArgumentTypes(typeof(string), typeof(int)));\n"})}),"\n",(0,i.jsx)(n.p,{children:"The exception message is:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Constructor not found for Namespace.Service with the given argument types: System.String, System.Int32.\n"})})]})}function p(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>o});t(6540);var i=t(870);const s={tabItem:"tabItem_Ymn6"};var r=t(4848);function o(e){let{children:n,hidden:t,className:o}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,i.A)(s.tabItem,o),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>w});var i=t(6540),s=t(870),r=t(3104),o=t(6347),a=t(205),c=t(7485),l=t(1682),d=t(9466);function u(e){return i.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,i.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:t}=e;return(0,i.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:t,attributes:i,default:s}}=e;return{value:n,label:t,attributes:i,default:s}}))}(t);return function(e){const n=(0,l.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function p(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function x(e){let{queryString:n=!1,groupId:t}=e;const s=(0,o.W6)(),r=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,c.aZ)(r),(0,i.useCallback)((e=>{if(!r)return;const n=new URLSearchParams(s.location.search);n.set(r,e),s.replace({...s.location,search:n.toString()})}),[r,s])]}function g(e){const{defaultValue:n,queryString:t=!1,groupId:s}=e,r=h(e),[o,c]=(0,i.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const i=t.find((e=>e.default))??t[0];if(!i)throw new Error("Unexpected error: 0 tabValues");return i.value}({defaultValue:n,tabValues:r}))),[l,u]=x({queryString:t,groupId:s}),[g,v]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[s,r]=(0,d.Dv)(t);return[s,(0,i.useCallback)((e=>{t&&r.set(e)}),[t,r])]}({groupId:s}),m=(()=>{const e=l??g;return p({value:e,tabValues:r})?e:null})();(0,a.A)((()=>{m&&c(m)}),[m]);return{selectedValue:o,selectValue:(0,i.useCallback)((e=>{if(!p({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);c(e),u(e),v(e)}),[u,v,r]),tabValues:r}}var v=t(2303);const m={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var f=t(4848);function j(e){let{className:n,block:t,selectedValue:i,selectValue:o,tabValues:a}=e;const c=[],{blockElementScrollPositionUntilNextRender:l}=(0,r.a_)(),d=e=>{const n=e.currentTarget,t=c.indexOf(n),s=a[t].value;s!==i&&(l(n),o(s))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const t=c.indexOf(e.currentTarget)+1;n=c[t]??c[0];break}case"ArrowLeft":{const t=c.indexOf(e.currentTarget)-1;n=c[t]??c[c.length-1];break}}n?.focus()};return(0,f.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":t},n),children:a.map((e=>{let{value:n,label:t,attributes:r}=e;return(0,f.jsx)("li",{role:"tab",tabIndex:i===n?0:-1,"aria-selected":i===n,ref:e=>c.push(e),onKeyDown:u,onClick:d,...r,className:(0,s.A)("tabs__item",m.tabItem,r?.className,{"tabs__item--active":i===n}),children:t??n},n)}))})}function y(e){let{lazy:n,children:t,selectedValue:s}=e;const r=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=r.find((e=>e.props.value===s));return e?(0,i.cloneElement)(e,{className:"margin-top--md"}):null}return(0,f.jsx)("div",{className:"margin-top--md",children:r.map(((e,n)=>(0,i.cloneElement)(e,{key:n,hidden:e.props.value!==s})))})}function b(e){const n=g(e);return(0,f.jsxs)("div",{className:(0,s.A)("tabs-container",m.tabList),children:[(0,f.jsx)(j,{...e,...n}),(0,f.jsx)(y,{...e,...n})]})}function w(e){const n=(0,v.A)();return(0,f.jsx)(b,{...e,children:u(e.children)},String(n))}},8453:(e,n,t)=>{t.d(n,{R:()=>o,x:()=>a});var i=t(6540);const s={},r=i.createContext(s);function o(e){const n=i.useContext(r);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),i.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/3a87badd.50d04665.js b/assets/js/3a87badd.706346b9.js similarity index 99% rename from assets/js/3a87badd.50d04665.js rename to assets/js/3a87badd.706346b9.js index 85410a0f..27a45de3 100644 --- a/assets/js/3a87badd.50d04665.js +++ b/assets/js/3a87badd.706346b9.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[803],{6958:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>h,contentTitle:()=>c,default:()=>g,frontMatter:()=>l,metadata:()=>d,toc:()=>p});var t=s(4848),r=s(8453),i=s(7470),o=s(1470),a=s(9365);const l={},c="Advanced registration",d={id:"guides/advanced-registration",title:"Advanced registration",description:"This section is about Stashbox's further configuration options, including the registration configuration API, the registration of factory delegates, multiple implementations, batch registrations, the concept of the Composition Root, and many more.",source:"@site/docs/guides/advanced-registration.md",sourceDirName:"guides",slug:"/guides/advanced-registration",permalink:"/stashbox/docs/guides/advanced-registration",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/guides/advanced-registration.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Basic usage",permalink:"/stashbox/docs/guides/basics"},next:{title:"Service resolution",permalink:"/stashbox/docs/guides/service-resolution"}},h={},p=[{value:"Factory registration",id:"factory-registration",level:2},{value:"Factories with parameter overrides",id:"factories-with-parameter-overrides",level:3},{value:"Consider this before using the resolver parameter inside a factory",id:"consider-this-before-using-the-resolver-parameter-inside-a-factory",level:3},{value:"Delegates with dependencies passed as parameters",id:"delegates-with-dependencies-passed-as-parameters",level:4},{value:"Accessing the currently resolving type in factories",id:"accessing-the-currently-resolving-type-in-factories",level:3},{value:"Multiple implementations",id:"multiple-implementations",level:2},{value:"Binding to multiple services",id:"binding-to-multiple-services",level:2},{value:"Batch registration",id:"batch-registration",level:2},{value:"Assembly registration",id:"assembly-registration",level:2},{value:"Composition root",id:"composition-root",level:2},{value:"Injection parameters",id:"injection-parameters",level:2},{value:"Initializer / finalizer",id:"initializer--finalizer",level:2}];function u(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",mdxAdmonitionTitle:"mdxAdmonitionTitle",p:"p",pre:"pre",strong:"strong",...(0,r.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{id:"advanced-registration",children:"Advanced registration"}),"\n",(0,t.jsxs)(n.p,{children:["This section is about Stashbox's further configuration options, including the registration configuration API, the registration of factory delegates, multiple implementations, batch registrations, the concept of the ",(0,t.jsx)(n.a,{href:"https://blog.ploeh.dk/2011/07/28/CompositionRoot/",children:"Composition Root"}),", and many more."]}),"\n",(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["This section won't cover all the available options of the registrations API, but you can find them ",(0,t.jsx)(n.a,{href:"/docs/configuration/registration-configuration",children:"here"}),"."]})}),"\n",(0,t.jsx)(n.h2,{id:"factory-registration",children:"Factory registration"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"You can bind a factory delegate to a registration that the container will invoke directly to instantiate your service."}),(0,t.jsxs)(n.p,{children:["You can use parameter-less and custom parameterized delegates as a factory. ",(0,t.jsx)(n.a,{href:"/docs/configuration/registration-configuration#factory",children:"Here"})," is the list of all available options."]}),(0,t.jsxs)(n.p,{children:["You can also get the current ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#dependency-resolver",children:"dependency resolver"})," as a delegate parameter to resolve any additional dependencies required for the service construction."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Parameter-less",label:"Parameter-less",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(options => options\n .WithFactory(() => new ConsoleLogger());\n\n// the container uses the factory for instantiation.\nIJob job = container.Resolve();\n"})})}),(0,t.jsx)(a.A,{value:"Parameterized",label:"Parameterized",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(options => options\n .WithFactory(logger => new DbBackup(logger));\n\n// the container uses the factory for instantiation.\nIJob job = container.Resolve();\n"})})}),(0,t.jsx)(a.A,{value:"Resolver parameter",label:"Resolver parameter",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(options => options\n .WithFactory(resolver => new DbBackup(resolver.Resolve()));\n \n// the container uses the factory for instantiation.\nIJob job = container.Resolve();\n"})})})]})})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"Delegate factories are useful when your service's instantiation is not straight-forward for the container, like when it depends on something that is not available at resolution time. E.g., a connection string."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.Register(options => options\n .WithFactory(logger => \n new DbBackup(Configuration["DbConnectionString"], logger));\n'})})})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"factories-with-parameter-overrides",children:"Factories with parameter overrides"}),(0,t.jsxs)(n.p,{children:["Stashbox can implicitly ",(0,t.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#delegate",children:"wrap"})," your service in a ",(0,t.jsx)(n.code,{children:"Delegate"})," and lets you pass parameters that can override your service's dependencies. Moreover, you can register your own custom delegate that the container will resolve when you request your service wrapped in a ",(0,t.jsx)(n.code,{children:"Delegate"}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(a.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.RegisterFunc((connectionString, resolver) => \n new DbBackup(connectionString, resolver.Resolve()));\n\nFunc backupFactory = container.Resolve>();\nIJob dbBackup = backupFactory(Configuration["ConnectionString"]);\n'})})}),(0,t.jsx)(a.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.RegisterFunc((connectionString, resolver) => \n new DbBackup(connectionString, resolver.Resolve()));\n\nDelegate backupFactory = container.ResolveFactory(typeof(IJob), \n parameterTypes: new[] { typeof(string) });\nIJob dbBackup = backupFactory.DynamicInvoke(Configuration["ConnectionString"]);\n'})})})]})})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["If a service has multiple constructors, the container visits those first, that has matching parameters passed to the factory, with respecting the additional ",(0,t.jsx)(n.a,{href:"/docs/configuration/registration-configuration#constructor-selection",children:"constructor selection rules"}),"."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"class Service\n{\n public Service(int number) { }\n public Service(string text) { }\n}\n\ncontainer.Register();\n\n// create the factory with an int input parameter.\nvar func = constainer.Resolve>();\n\n// the constructor with the int param \n// is used for instantiation.\nvar service = func(2);\n"})})})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"consider-this-before-using-the-resolver-parameter-inside-a-factory",children:"Consider this before using the resolver parameter inside a factory"}),(0,t.jsxs)(n.p,{children:["Delegate factories are a black-box for the container. It doesn't have control over what's happening inside a delegate, which means when you resolve additional dependencies with the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#dependency-resolver",children:"dependency resolver"})," parameter, they could easily bypass the ",(0,t.jsx)(n.a,{href:"/docs/diagnostics/validation#lifetime-validation",children:"lifetime"})," and ",(0,t.jsx)(n.a,{href:"/docs/diagnostics/validation#circular-dependency",children:"circular dependency"})," validations. Fortunately, you have the option to keep them validated anyway with parameterized factory delegates."]}),(0,t.jsx)(n.h4,{id:"delegates-with-dependencies-passed-as-parameters",children:"Delegates with dependencies passed as parameters"}),(0,t.jsxs)(n.p,{children:["Rather than using the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#dependency-resolver",children:"dependency resolver"})," parameter inside the factory, let the container inject the dependencies into the delegate as parameters. This way, the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree's"})," integrity remains stable because no service resolution happens inside the black-box, and each parameter is validated."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"interface IEventProcessor { }\n\nclass EventProcessor : IEventProcessor\n{\n public EventProcessor(ILogger logger, IEventValidator validator)\n { }\n}\n\ncontainer.Register();\ncontainer.Register();\n\ncontainer.Register(options => options\n // Ilogger and IEventValidator instances are injected\n // by the container at resolution time, so they will be\n // validated against circular and captive dependencies.\n .WithFactory((logger, validator) => \n new EventProcessor(logger, validator));\n\n// the container resolves ILogger and IEventValidator first, then\n// it passes them to the factory as delegate parameters.\nIEventProcessor processor = container.Resolve();\n"})})})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"accessing-the-currently-resolving-type-in-factories",children:"Accessing the currently resolving type in factories"}),(0,t.jsxs)(n.p,{children:["To access the currently resolving type in factory delegates, you can set the ",(0,t.jsx)(n.code,{children:"TypeInformation"})," type as an input parameter of the factory.\nThe ",(0,t.jsx)(n.code,{children:"TypeInformation"})," holds every reflected context information about the currently resolving type."]}),(0,t.jsx)(n.p,{children:"This can be useful when the resolution is, e.g., in an open generic context, and we want to know which closed generic variant is requested."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"interface IService { }\n\nclass Service : IService { }\n\ncontainer.Register(typeof(IService<>), typeof(Service<>), options => \n options.WithFactory(typeInfo => \n {\n // typeInfo.Type here holds the actual type like\n // IService based on the resolution request below.\n }));\n \ncontainer.Resolve>();\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"multiple-implementations",children:"Multiple implementations"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["As we previously saw in the ",(0,t.jsx)(n.a,{href:"/docs/guides/basics#named-registration",children:"Named registration"})," topic, Stashbox allows you to have multiple implementations bound to a particular ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),". You can use names to distinguish them, but you can also access them by requesting a typed collection using the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),"."]}),(0,t.jsxs)(n.admonition,{type:"note",children:[(0,t.jsx)(n.mdxAdmonitionTitle,{}),(0,t.jsx)(n.p,{children:"The returned collection is in the same order as the services were registered.\nAlso, to request a collection, you can use any interface implemented by an array."})]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\ncontainer.Register();\ncontainer.Register();\n"})}),(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"ResolveAll",label:"ResolveAll",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// jobs contain all three services in registration order.\nIEnumerable jobs = container.ResolveAll();\n"})})}),(0,t.jsx)(a.A,{value:"Array",label:"Array",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// jobs contain all three services in registration order.\nIJob[] jobs = container.Resolve();\n"})})}),(0,t.jsx)(a.A,{value:"IEnumerable",label:"IEnumerable",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// jobs contain all three services in registration order.\nIEnumerable jobs = container.Resolve>();\n"})})}),(0,t.jsx)(a.A,{value:"IList",label:"IList",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// jobs contain all three services in registration order.\nIList jobs = container.Resolve>();\n"})})}),(0,t.jsx)(a.A,{value:"ICollection",label:"ICollection",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// jobs contain all three services in registration order.\nICollection jobs = container.Resolve>();\n"})})})]})]})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["When you have multiple implementations registered to a service, a request to the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," without a name will return the ",(0,t.jsx)(n.strong,{children:"last registered implementation"}),"."]}),(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["Not only names can be used to distinguish registrations, ",(0,t.jsx)(n.a,{href:"/docs/guides/service-resolution#conditional-resolution",children:"conditions"}),", ",(0,t.jsx)(n.a,{href:"/docs/guides/scopes#named-scopes",children:"named scopes"}),", and ",(0,t.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#metadata--tuple",children:"metadata"})," can also influence the results."]})})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\ncontainer.Register();\ncontainer.Register();\n\n// job will be the ImageProcess.\nIJob job = container.Resolve();\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"binding-to-multiple-services",children:"Binding to multiple services"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"When you have a service that implements multiple interfaces, you have the option to bind its registration to all or some of those additional interfaces or base types."}),(0,t.jsx)(n.p,{children:"Suppose we have the following class declaration:"}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob, IScheduledJob\n{ \n public DbBackup() { }\n}\n"})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"To another type",label:"To another type",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(options => options\n .AsServiceAlso());\n\nIJob job = container.Resolve(); // DbBackup\nIScheduledJob job = container.Resolve(); // DbBackup\nDbBackup job = container.Resolve(); // error, not found\n"})})}),(0,t.jsx)(a.A,{value:"To all implemented types",label:"To all implemented types",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(options => options\n .AsImplementedTypes());\n\nIJob job = container.Resolve(); // DbBackup\nIScheduledJob job = container.Resolve(); // DbBackup\nDbBackup job = container.Resolve(); // DbBackup\n"})})})]})})]}),"\n",(0,t.jsx)(n.h2,{id:"batch-registration",children:"Batch registration"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"You have the option to register multiple services in a single registration operation."}),(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Filters (optional):"}),"\nFirst, the container will use the ",(0,t.jsx)(n.em,{children:"implementation filter"})," action to select only those types from the collection we want to register. When we have those, the container will execute the ",(0,t.jsx)(n.em,{children:"service filter"})," on their implemented interfaces and base classes to select which ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," they should be mapped to."]}),(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["Framework types like ",(0,t.jsx)(n.code,{children:"IDisposable"})," are excluded from being considered as a ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," by default."]})}),(0,t.jsxs)(n.admonition,{type:"tip",children:[(0,t.jsx)(n.mdxAdmonitionTitle,{}),(0,t.jsx)(n.p,{children:"You can use the registration configuration API to configure individual registrations."})]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{children:[(0,t.jsxs)(a.A,{value:"Default",label:"Default",children:[(0,t.jsxs)(n.p,{children:["This example will register three types to all their implemented interfaces, extended base classes, and to themselves (",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#self-registration",children:"self registration"}),") without any filter:"]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterTypes(new[] \n { \n typeof(DbBackup), \n typeof(ConsoleLogger), \n typeof(StorageCleanup) \n });\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nILogger logger = container.Resolve(); // ConsoleLogger\nIJob job = container.Resolve(); // StorageCleanup\nDbBackup backup = container.Resolve(); // DbBackup\n"})})]}),(0,t.jsxs)(a.A,{value:"Filters",label:"Filters",children:[(0,t.jsxs)(n.p,{children:["In this example, we assume that ",(0,t.jsx)(n.code,{children:"DbBackup"})," and ",(0,t.jsx)(n.code,{children:"StorageCleanup"})," are implementing ",(0,t.jsx)(n.code,{children:"IDisposable"})," besides ",(0,t.jsx)(n.code,{children:"IJob"})," and also extending a ",(0,t.jsx)(n.code,{children:"JobBase"})," abstract class."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterTypes(new[] \n { typeof(DbBackup), typeof(ConsoleLogger), typeof(StorageCleanup) },\n // implementation filter, only those implementations that implements IDisposable\n impl => typeof(IDisposable).IsAssignableFrom(impl),\n // service filter, register them to base classes only\n (impl, service) => service.IsAbstract && !service.IsInterface);\n\nIEnumerable jobs = container.ResolveAll(); // 0 items\nIEnumerable jobs = container.ResolveAll(); // 2 items\nILogger logger = container.Resolve(); // error, not found\nDbBackup backup = container.Resolve(); // DbBackup\n"})})]}),(0,t.jsxs)(a.A,{value:"Without self",label:"Without self",children:[(0,t.jsxs)(n.p,{children:["This example ignores the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#self-registration",children:"self registrations"})," completely:"]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterTypes(new[] \n { \n typeof(DbBackup), \n typeof(ConsoleLogger), \n typeof(StorageCleanup)\n },\n registerSelf: false);\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nILogger logger = container.Resolve(); // ConsoleLogger\nDbBackup backup = container.Resolve(); // error, not found\nConsoleLogger logger = container.Resolve(); // error, not found\n"})})]}),(0,t.jsxs)(a.A,{value:"Registration options",label:"Registration options",children:[(0,t.jsxs)(n.p,{children:["This example will configure all registrations mapped to ",(0,t.jsx)(n.code,{children:"ILogger"})," as ",(0,t.jsx)(n.code,{children:"Singleton"}),":"]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterTypes(new[] \n { \n typeof(DbBackup), \n typeof(ConsoleLogger), \n typeof(StorageCleanup)\n },\n configurator: options => \n {\n if (options.HasServiceType())\n options.WithSingletonLifetime();\n });\n\nILogger logger = container.Resolve(); // ConsoleLogger\nILogger newLogger = container.Resolve(); // the same ConsoleLogger\nIEnumerable jobs = container.ResolveAll(); // 2 items\n"})})]})]})})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["Another type of service filter is the ",(0,t.jsx)(n.code,{children:".RegisterTypesAs()"})," method, which registers only those types that implements the ",(0,t.jsx)(n.code,{children:"T"})," ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),"."]}),(0,t.jsxs)(n.admonition,{type:"note",children:[(0,t.jsx)(n.mdxAdmonitionTitle,{}),(0,t.jsxs)(n.p,{children:["This method also accepts an implementation filter and a registration configurator action like ",(0,t.jsx)(n.code,{children:".RegisterTypes()"}),"."]})]}),(0,t.jsx)(n.admonition,{type:"caution",children:(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.code,{children:".RegisterTypesAs()"})," doesn't create ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#self-registration",children:"self registrations"})," as it only maps the implementations to the given ",(0,t.jsx)(n.code,{children:"T"})," ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),"."]})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(a.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterTypesAs(new[] \n { \n typeof(DbBackup), \n typeof(ConsoleLogger), \n typeof(StorageCleanup) \n });\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nILogger logger = container.Resolve(); // error, not found\nIJob job = container.Resolve(); // StorageCleanup\nDbBackup backup = container.Resolve(); // error, not found\n"})})}),(0,t.jsx)(a.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterTypesAs(typeof(IJob), new[] \n { \n typeof(DbBackup), \n typeof(ConsoleLogger), \n typeof(StorageCleanup) \n });\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nILogger logger = container.Resolve(); // error, not found\nIJob job = container.Resolve(); // StorageCleanup\nDbBackup backup = container.Resolve(); // error, not found\n"})})})]})})]}),"\n",(0,t.jsx)(n.h2,{id:"assembly-registration",children:"Assembly registration"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["The batch registration API ",(0,t.jsx)(n.em,{children:"(filters, registration configuration action, self-registration)"})," is also usable for registering services from given assemblies."]}),(0,t.jsxs)(n.p,{children:["In this example, we assume that the same three services we used in the ",(0,t.jsx)(n.a,{href:"#batch-registration",children:"batch registration"})," section are in the same assembly."]}),(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["The container also detects and registers open-generic definitions (when applicable) from the supplied type collection. You can read about ",(0,t.jsx)(n.a,{href:"/docs/advanced/generics#open-generics",children:"open-generics here"}),"."]})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Single assembly",label:"Single assembly",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterAssembly(typeof(DbBackup).Assembly,\n // service filter, register to interfaces only\n serviceTypeSelector: (impl, service) => service.IsInterface,\n registerSelf: false,\n configurator: options => options.WithoutDisposalTracking()\n);\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nIEnumerable jobs = container.ResolveAll(); // 0 items\nILogger logger = container.Resolve(); // ConsoleLogger\nDbBackup backup = container.Resolve(); // error, not found\n"})})}),(0,t.jsx)(a.A,{value:"Multiple assemblies",label:"Multiple assemblies",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterAssemblies(new[] \n { \n typeof(DbBackup).Assembly, \n typeof(JobFromAnotherAssembly).Assembly \n },\n // service filter, register to interfaces only\n serviceTypeSelector: (impl, service) => service.IsInterface,\n registerSelf: false,\n configurator: options => options.WithoutDisposalTracking()\n);\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nIEnumerable jobs = container.ResolveAll(); // 0 items\nILogger logger = container.Resolve(); // ConsoleLogger\nDbBackup backup = container.Resolve(); // error, not found\n"})})}),(0,t.jsx)(a.A,{value:"Containing type",label:"Containing type",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterAssemblyContaining(\n // service filter, register to interfaces only\n serviceTypeSelector: (impl, service) => service.IsInterface,\n registerSelf: false,\n configurator: options => options.WithoutDisposalTracking()\n);\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nIEnumerable jobs = container.ResolveAll(); // 0 items\nILogger logger = container.Resolve(); // ConsoleLogger\nDbBackup backup = container.Resolve(); // error, not found\n"})})})]})})]}),"\n",(0,t.jsx)(n.h2,{id:"composition-root",children:"Composition root"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.a,{href:"https://blog.ploeh.dk/2011/07/28/CompositionRoot/",children:"Composition Root"})," is an entry point where all services required to make a component functional are wired together."]}),(0,t.jsxs)(n.p,{children:["Stashbox provides an ",(0,t.jsx)(n.code,{children:"ICompositionRoot"})," interface that can be used to define an entry point for a given component or even for an entire assembly."]}),(0,t.jsxs)(n.p,{children:["You can wire up your ",(0,t.jsx)(n.em,{children:"composition root"})," implementation with ",(0,t.jsx)(n.code,{children:"ComposeBy()"}),", or you can let the container find and execute all available ",(0,t.jsx)(n.em,{children:"composition root"})," implementations within an assembly."]}),(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["Your ",(0,t.jsx)(n.code,{children:"ICompositionRoot"})," implementation also can have dependencies that the container will resolve."]})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"class ExampleRoot : ICompositionRoot\n{\n public ExampleRoot(IDependency rootDependency)\n { }\n\n public void Compose(IStashboxContainer container)\n {\n container.Register();\n container.Register();\n }\n}\n"})}),(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Single",label:"Single",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// compose a single root.\ncontainer.ComposeBy();\n"})})}),(0,t.jsx)(a.A,{value:"Assembly",label:"Assembly",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// compose every root in the given assembly.\ncontainer.ComposeAssembly(typeof(IServiceA).Assembly);\n"})})}),(0,t.jsx)(a.A,{value:"Override",label:"Override",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// compose a single root with dependency override.\ncontainer.ComposeBy(new CustomRootDependency());\n"})})})]})]})]}),"\n",(0,t.jsx)(n.h2,{id:"injection-parameters",children:"Injection parameters"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"If you have pre-evaluated dependencies you'd like to inject at resolution time, you can set them as injection parameters during registration."}),(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsx)(n.p,{children:"Injection parameter names are matched to constructor arguments or field/property names."})})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.Register(options => options\n .WithInjectionParameter("logger", new ConsoleLogger())\n .WithInjectionParameter("eventBroadcaster", new MessageBus());\n\n// the injection parameters will be passed to DbBackup\'s constructor.\nIJob backup = container.Resolve();\n'})})})]}),"\n",(0,t.jsx)(n.h2,{id:"initializer--finalizer",children:"Initializer / finalizer"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"The container provides specific extension points to let you react to lifetime events of an instantiated service."}),(0,t.jsxs)(n.p,{children:["For this reason, you can specify ",(0,t.jsx)(n.em,{children:"Initializer"})," and ",(0,t.jsx)(n.em,{children:"Finalizer"})," delegates. The ",(0,t.jsx)(n.em,{children:"finalizer"})," is called upon the service's ",(0,t.jsx)(n.a,{href:"/docs/guides/scopes#disposal",children:"disposal"}),", and the ",(0,t.jsx)(n.em,{children:"initializer"})," is called upon the service's construction."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(options => options\n // delegate that called right after instantiation.\n .WithInitializer((logger, resolver) => logger.OpenFile())\n // delegate that called right before the instance's disposal.\n .WithFinalizer(logger => logger.CloseFile()));\n"})})})]})]})}function g(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(u,{...e})}):u(e)}},9365:(e,n,s)=>{s.d(n,{A:()=>o});s(6540);var t=s(870);const r={tabItem:"tabItem_Ymn6"};var i=s(4848);function o(e){let{children:n,hidden:s,className:o}=e;return(0,i.jsx)("div",{role:"tabpanel",className:(0,t.A)(r.tabItem,o),hidden:s,children:n})}},1470:(e,n,s)=>{s.d(n,{A:()=>I});var t=s(6540),r=s(870),i=s(3104),o=s(6347),a=s(205),l=s(7485),c=s(1682),d=s(9466);function h(e){return t.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,t.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function p(e){const{values:n,children:s}=e;return(0,t.useMemo)((()=>{const e=n??function(e){return h(e).map((e=>{let{props:{value:n,label:s,attributes:t,default:r}}=e;return{value:n,label:s,attributes:t,default:r}}))}(s);return function(e){const n=(0,c.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,s])}function u(e){let{value:n,tabValues:s}=e;return s.some((e=>e.value===n))}function g(e){let{queryString:n=!1,groupId:s}=e;const r=(0,o.W6)(),i=function(e){let{queryString:n=!1,groupId:s}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!s)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return s??null}({queryString:n,groupId:s});return[(0,l.aZ)(i),(0,t.useCallback)((e=>{if(!i)return;const n=new URLSearchParams(r.location.search);n.set(i,e),r.replace({...r.location,search:n.toString()})}),[i,r])]}function m(e){const{defaultValue:n,queryString:s=!1,groupId:r}=e,i=p(e),[o,l]=(0,t.useState)((()=>function(e){let{defaultValue:n,tabValues:s}=e;if(0===s.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!u({value:n,tabValues:s}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${s.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const t=s.find((e=>e.default))??s[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:i}))),[c,h]=g({queryString:s,groupId:r}),[m,b]=function(e){let{groupId:n}=e;const s=function(e){return e?`docusaurus.tab.${e}`:null}(n),[r,i]=(0,d.Dv)(s);return[r,(0,t.useCallback)((e=>{s&&i.set(e)}),[s,i])]}({groupId:r}),v=(()=>{const e=c??m;return u({value:e,tabValues:i})?e:null})();(0,a.A)((()=>{v&&l(v)}),[v]);return{selectedValue:o,selectValue:(0,t.useCallback)((e=>{if(!u({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);l(e),h(e),b(e)}),[h,b,i]),tabValues:i}}var b=s(2303);const v={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var j=s(4848);function x(e){let{className:n,block:s,selectedValue:t,selectValue:o,tabValues:a}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,i.a_)(),d=e=>{const n=e.currentTarget,s=l.indexOf(n),r=a[s].value;r!==t&&(c(n),o(r))},h=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const s=l.indexOf(e.currentTarget)+1;n=l[s]??l[0];break}case"ArrowLeft":{const s=l.indexOf(e.currentTarget)-1;n=l[s]??l[l.length-1];break}}n?.focus()};return(0,j.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":s},n),children:a.map((e=>{let{value:n,label:s,attributes:i}=e;return(0,j.jsx)("li",{role:"tab",tabIndex:t===n?0:-1,"aria-selected":t===n,ref:e=>l.push(e),onKeyDown:h,onClick:d,...i,className:(0,r.A)("tabs__item",v.tabItem,i?.className,{"tabs__item--active":t===n}),children:s??n},n)}))})}function f(e){let{lazy:n,children:s,selectedValue:r}=e;const i=(Array.isArray(s)?s:[s]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===r));return e?(0,t.cloneElement)(e,{className:"margin-top--md"}):null}return(0,j.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,t.cloneElement)(e,{key:n,hidden:e.props.value!==r})))})}function y(e){const n=m(e);return(0,j.jsxs)("div",{className:(0,r.A)("tabs-container",v.tabList),children:[(0,j.jsx)(x,{...e,...n}),(0,j.jsx)(f,{...e,...n})]})}function I(e){const n=(0,b.A)();return(0,j.jsx)(y,{...e,children:h(e.children)},String(n))}},7470:(e,n,s)=>{s.d(n,{A:()=>o});var t=s(6540);const r={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var i=s(4848);function o(e){let{children:n}=e,s=t.Children.toArray(n).filter((e=>e));return(0,i.jsxs)("div",{className:r.codeDescContainer,children:[(0,i.jsx)("div",{className:r.desc,children:s[0]}),(0,i.jsx)("div",{className:r.example,children:s[1]})]})}},8453:(e,n,s)=>{s.d(n,{R:()=>o,x:()=>a});var t=s(6540);const r={},i=t.createContext(r);function o(e){const n=t.useContext(i);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),t.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[803],{6958:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>h,contentTitle:()=>c,default:()=>g,frontMatter:()=>l,metadata:()=>d,toc:()=>p});var t=s(4848),r=s(8453),i=s(7470),o=s(1470),a=s(9365);const l={},c="Advanced registration",d={id:"guides/advanced-registration",title:"Advanced registration",description:"This section is about Stashbox's further configuration options, including the registration configuration API, the registration of factory delegates, multiple implementations, batch registrations, the concept of the Composition Root, and many more.",source:"@site/docs/guides/advanced-registration.md",sourceDirName:"guides",slug:"/guides/advanced-registration",permalink:"/stashbox/docs/guides/advanced-registration",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/guides/advanced-registration.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Basic usage",permalink:"/stashbox/docs/guides/basics"},next:{title:"Service resolution",permalink:"/stashbox/docs/guides/service-resolution"}},h={},p=[{value:"Factory registration",id:"factory-registration",level:2},{value:"Factories with parameter overrides",id:"factories-with-parameter-overrides",level:3},{value:"Consider this before using the resolver parameter inside a factory",id:"consider-this-before-using-the-resolver-parameter-inside-a-factory",level:3},{value:"Delegates with dependencies passed as parameters",id:"delegates-with-dependencies-passed-as-parameters",level:4},{value:"Accessing the currently resolving type in factories",id:"accessing-the-currently-resolving-type-in-factories",level:3},{value:"Multiple implementations",id:"multiple-implementations",level:2},{value:"Binding to multiple services",id:"binding-to-multiple-services",level:2},{value:"Batch registration",id:"batch-registration",level:2},{value:"Assembly registration",id:"assembly-registration",level:2},{value:"Composition root",id:"composition-root",level:2},{value:"Injection parameters",id:"injection-parameters",level:2},{value:"Initializer / finalizer",id:"initializer--finalizer",level:2}];function u(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",mdxAdmonitionTitle:"mdxAdmonitionTitle",p:"p",pre:"pre",strong:"strong",...(0,r.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{id:"advanced-registration",children:"Advanced registration"}),"\n",(0,t.jsxs)(n.p,{children:["This section is about Stashbox's further configuration options, including the registration configuration API, the registration of factory delegates, multiple implementations, batch registrations, the concept of the ",(0,t.jsx)(n.a,{href:"https://blog.ploeh.dk/2011/07/28/CompositionRoot/",children:"Composition Root"}),", and many more."]}),"\n",(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["This section won't cover all the available options of the registrations API, but you can find them ",(0,t.jsx)(n.a,{href:"/docs/configuration/registration-configuration",children:"here"}),"."]})}),"\n",(0,t.jsx)(n.h2,{id:"factory-registration",children:"Factory registration"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"You can bind a factory delegate to a registration that the container will invoke directly to instantiate your service."}),(0,t.jsxs)(n.p,{children:["You can use parameter-less and custom parameterized delegates as a factory. ",(0,t.jsx)(n.a,{href:"/docs/configuration/registration-configuration#factory",children:"Here"})," is the list of all available options."]}),(0,t.jsxs)(n.p,{children:["You can also get the current ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#dependency-resolver",children:"dependency resolver"})," as a delegate parameter to resolve any additional dependencies required for the service construction."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Parameter-less",label:"Parameter-less",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(options => options\n .WithFactory(() => new ConsoleLogger());\n\n// the container uses the factory for instantiation.\nIJob job = container.Resolve();\n"})})}),(0,t.jsx)(a.A,{value:"Parameterized",label:"Parameterized",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(options => options\n .WithFactory(logger => new DbBackup(logger));\n\n// the container uses the factory for instantiation.\nIJob job = container.Resolve();\n"})})}),(0,t.jsx)(a.A,{value:"Resolver parameter",label:"Resolver parameter",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(options => options\n .WithFactory(resolver => new DbBackup(resolver.Resolve()));\n \n// the container uses the factory for instantiation.\nIJob job = container.Resolve();\n"})})})]})})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"Delegate factories are useful when your service's instantiation is not straight-forward for the container, like when it depends on something that is not available at resolution time. E.g., a connection string."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.Register(options => options\n .WithFactory(logger => \n new DbBackup(Configuration["DbConnectionString"], logger));\n'})})})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"factories-with-parameter-overrides",children:"Factories with parameter overrides"}),(0,t.jsxs)(n.p,{children:["Stashbox can implicitly ",(0,t.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#delegate",children:"wrap"})," your service in a ",(0,t.jsx)(n.code,{children:"Delegate"})," and lets you pass parameters that can override your service's dependencies. Moreover, you can register your own custom delegate that the container will resolve when you request your service wrapped in a ",(0,t.jsx)(n.code,{children:"Delegate"}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(a.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.RegisterFunc((connectionString, resolver) => \n new DbBackup(connectionString, resolver.Resolve()));\n\nFunc backupFactory = container.Resolve>();\nIJob dbBackup = backupFactory(Configuration["ConnectionString"]);\n'})})}),(0,t.jsx)(a.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.RegisterFunc((connectionString, resolver) => \n new DbBackup(connectionString, resolver.Resolve()));\n\nDelegate backupFactory = container.ResolveFactory(typeof(IJob), \n parameterTypes: new[] { typeof(string) });\nIJob dbBackup = backupFactory.DynamicInvoke(Configuration["ConnectionString"]);\n'})})})]})})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["If a service has multiple constructors, the container visits those first, that has matching parameters passed to the factory, with respecting the additional ",(0,t.jsx)(n.a,{href:"/docs/configuration/registration-configuration#constructor-selection",children:"constructor selection rules"}),"."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"class Service\n{\n public Service(int number) { }\n public Service(string text) { }\n}\n\ncontainer.Register();\n\n// create the factory with an int input parameter.\nvar func = constainer.Resolve>();\n\n// the constructor with the int param \n// is used for instantiation.\nvar service = func(2);\n"})})})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"consider-this-before-using-the-resolver-parameter-inside-a-factory",children:"Consider this before using the resolver parameter inside a factory"}),(0,t.jsxs)(n.p,{children:["Delegate factories are a black-box for the container. It doesn't have control over what's happening inside a delegate, which means when you resolve additional dependencies with the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#dependency-resolver",children:"dependency resolver"})," parameter, they could easily bypass the ",(0,t.jsx)(n.a,{href:"/docs/diagnostics/validation#lifetime-validation",children:"lifetime"})," and ",(0,t.jsx)(n.a,{href:"/docs/diagnostics/validation#circular-dependency",children:"circular dependency"})," validations. Fortunately, you have the option to keep them validated anyway with parameterized factory delegates."]}),(0,t.jsx)(n.h4,{id:"delegates-with-dependencies-passed-as-parameters",children:"Delegates with dependencies passed as parameters"}),(0,t.jsxs)(n.p,{children:["Rather than using the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#dependency-resolver",children:"dependency resolver"})," parameter inside the factory, let the container inject the dependencies into the delegate as parameters. This way, the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree's"})," integrity remains stable because no service resolution happens inside the black-box, and each parameter is validated."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"interface IEventProcessor { }\n\nclass EventProcessor : IEventProcessor\n{\n public EventProcessor(ILogger logger, IEventValidator validator)\n { }\n}\n\ncontainer.Register();\ncontainer.Register();\n\ncontainer.Register(options => options\n // Ilogger and IEventValidator instances are injected\n // by the container at resolution time, so they will be\n // validated against circular and captive dependencies.\n .WithFactory((logger, validator) => \n new EventProcessor(logger, validator));\n\n// the container resolves ILogger and IEventValidator first, then\n// it passes them to the factory as delegate parameters.\nIEventProcessor processor = container.Resolve();\n"})})})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"accessing-the-currently-resolving-type-in-factories",children:"Accessing the currently resolving type in factories"}),(0,t.jsxs)(n.p,{children:["To access the currently resolving type in factory delegates, you can set the ",(0,t.jsx)(n.code,{children:"TypeInformation"})," type as an input parameter of the factory.\nThe ",(0,t.jsx)(n.code,{children:"TypeInformation"})," holds every reflected context information about the currently resolving type."]}),(0,t.jsx)(n.p,{children:"This can be useful when the resolution is, e.g., in an open generic context, and we want to know which closed generic variant is requested."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"interface IService { }\n\nclass Service : IService { }\n\ncontainer.Register(typeof(IService<>), typeof(Service<>), options => \n options.WithFactory(typeInfo => \n {\n // typeInfo.Type here holds the actual type like\n // IService based on the resolution request below.\n }));\n \ncontainer.Resolve>();\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"multiple-implementations",children:"Multiple implementations"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["As we previously saw in the ",(0,t.jsx)(n.a,{href:"/docs/guides/basics#named-registration",children:"Named registration"})," topic, Stashbox allows you to have multiple implementations bound to a particular ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),". You can use names to distinguish them, but you can also access them by requesting a typed collection using the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),"."]}),(0,t.jsxs)(n.admonition,{type:"note",children:[(0,t.jsx)(n.mdxAdmonitionTitle,{}),(0,t.jsx)(n.p,{children:"The returned collection is in the same order as the services were registered.\nAlso, to request a collection, you can use any interface implemented by an array."})]})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\ncontainer.Register();\ncontainer.Register();\n"})}),(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"ResolveAll",label:"ResolveAll",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// jobs contain all three services in registration order.\nIEnumerable jobs = container.ResolveAll();\n"})})}),(0,t.jsx)(a.A,{value:"Array",label:"Array",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// jobs contain all three services in registration order.\nIJob[] jobs = container.Resolve();\n"})})}),(0,t.jsx)(a.A,{value:"IEnumerable",label:"IEnumerable",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// jobs contain all three services in registration order.\nIEnumerable jobs = container.Resolve>();\n"})})}),(0,t.jsx)(a.A,{value:"IList",label:"IList",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// jobs contain all three services in registration order.\nIList jobs = container.Resolve>();\n"})})}),(0,t.jsx)(a.A,{value:"ICollection",label:"ICollection",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// jobs contain all three services in registration order.\nICollection jobs = container.Resolve>();\n"})})})]})]})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["When you have multiple implementations registered to a service, a request to the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," without a name will return the ",(0,t.jsx)(n.strong,{children:"last registered implementation"}),"."]}),(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["Not only names can be used to distinguish registrations, ",(0,t.jsx)(n.a,{href:"/docs/guides/service-resolution#conditional-resolution",children:"conditions"}),", ",(0,t.jsx)(n.a,{href:"/docs/guides/scopes#named-scopes",children:"named scopes"}),", and ",(0,t.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#metadata--tuple",children:"metadata"})," can also influence the results."]})})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\ncontainer.Register();\ncontainer.Register();\n\n// job will be the ImageProcess.\nIJob job = container.Resolve();\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"binding-to-multiple-services",children:"Binding to multiple services"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"When you have a service that implements multiple interfaces, you have the option to bind its registration to all or some of those additional interfaces or base types."}),(0,t.jsx)(n.p,{children:"Suppose we have the following class declaration:"}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"class DbBackup : IJob, IScheduledJob\n{ \n public DbBackup() { }\n}\n"})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"To another type",label:"To another type",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(options => options\n .AsServiceAlso());\n\nIJob job = container.Resolve(); // DbBackup\nIScheduledJob job = container.Resolve(); // DbBackup\nDbBackup job = container.Resolve(); // error, not found\n"})})}),(0,t.jsx)(a.A,{value:"To all implemented types",label:"To all implemented types",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(options => options\n .AsImplementedTypes());\n\nIJob job = container.Resolve(); // DbBackup\nIScheduledJob job = container.Resolve(); // DbBackup\nDbBackup job = container.Resolve(); // DbBackup\n"})})})]})})]}),"\n",(0,t.jsx)(n.h2,{id:"batch-registration",children:"Batch registration"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"You have the option to register multiple services in a single registration operation."}),(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Filters (optional):"}),"\nFirst, the container will use the ",(0,t.jsx)(n.em,{children:"implementation filter"})," action to select only those types from the collection we want to register. When we have those, the container will execute the ",(0,t.jsx)(n.em,{children:"service filter"})," on their implemented interfaces and base classes to select which ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," they should be mapped to."]}),(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["Framework types like ",(0,t.jsx)(n.code,{children:"IDisposable"})," are excluded from being considered as a ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," by default."]})}),(0,t.jsxs)(n.admonition,{type:"tip",children:[(0,t.jsx)(n.mdxAdmonitionTitle,{}),(0,t.jsx)(n.p,{children:"You can use the registration configuration API to configure individual registrations."})]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{children:[(0,t.jsxs)(a.A,{value:"Default",label:"Default",children:[(0,t.jsxs)(n.p,{children:["This example will register three types to all their implemented interfaces, extended base classes, and to themselves (",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#self-registration",children:"self registration"}),") without any filter:"]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterTypes(new[] \n { \n typeof(DbBackup), \n typeof(ConsoleLogger), \n typeof(StorageCleanup) \n });\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nILogger logger = container.Resolve(); // ConsoleLogger\nIJob job = container.Resolve(); // StorageCleanup\nDbBackup backup = container.Resolve(); // DbBackup\n"})})]}),(0,t.jsxs)(a.A,{value:"Filters",label:"Filters",children:[(0,t.jsxs)(n.p,{children:["In this example, we assume that ",(0,t.jsx)(n.code,{children:"DbBackup"})," and ",(0,t.jsx)(n.code,{children:"StorageCleanup"})," are implementing ",(0,t.jsx)(n.code,{children:"IDisposable"})," besides ",(0,t.jsx)(n.code,{children:"IJob"})," and also extending a ",(0,t.jsx)(n.code,{children:"JobBase"})," abstract class."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterTypes(new[] \n { typeof(DbBackup), typeof(ConsoleLogger), typeof(StorageCleanup) },\n // implementation filter, only those implementations that implements IDisposable\n impl => typeof(IDisposable).IsAssignableFrom(impl),\n // service filter, register them to base classes only\n (impl, service) => service.IsAbstract && !service.IsInterface);\n\nIEnumerable jobs = container.ResolveAll(); // 0 items\nIEnumerable jobs = container.ResolveAll(); // 2 items\nILogger logger = container.Resolve(); // error, not found\nDbBackup backup = container.Resolve(); // DbBackup\n"})})]}),(0,t.jsxs)(a.A,{value:"Without self",label:"Without self",children:[(0,t.jsxs)(n.p,{children:["This example ignores the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#self-registration",children:"self registrations"})," completely:"]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterTypes(new[] \n { \n typeof(DbBackup), \n typeof(ConsoleLogger), \n typeof(StorageCleanup)\n },\n registerSelf: false);\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nILogger logger = container.Resolve(); // ConsoleLogger\nDbBackup backup = container.Resolve(); // error, not found\nConsoleLogger logger = container.Resolve(); // error, not found\n"})})]}),(0,t.jsxs)(a.A,{value:"Registration options",label:"Registration options",children:[(0,t.jsxs)(n.p,{children:["This example will configure all registrations mapped to ",(0,t.jsx)(n.code,{children:"ILogger"})," as ",(0,t.jsx)(n.code,{children:"Singleton"}),":"]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterTypes(new[] \n { \n typeof(DbBackup), \n typeof(ConsoleLogger), \n typeof(StorageCleanup)\n },\n configurator: options => \n {\n if (options.HasServiceType())\n options.WithSingletonLifetime();\n });\n\nILogger logger = container.Resolve(); // ConsoleLogger\nILogger newLogger = container.Resolve(); // the same ConsoleLogger\nIEnumerable jobs = container.ResolveAll(); // 2 items\n"})})]})]})})]}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["Another type of service filter is the ",(0,t.jsx)(n.code,{children:".RegisterTypesAs()"})," method, which registers only those types that implements the ",(0,t.jsx)(n.code,{children:"T"})," ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),"."]}),(0,t.jsxs)(n.admonition,{type:"note",children:[(0,t.jsx)(n.mdxAdmonitionTitle,{}),(0,t.jsxs)(n.p,{children:["This method also accepts an implementation filter and a registration configurator action like ",(0,t.jsx)(n.code,{children:".RegisterTypes()"}),"."]})]}),(0,t.jsx)(n.admonition,{type:"caution",children:(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.code,{children:".RegisterTypesAs()"})," doesn't create ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#self-registration",children:"self registrations"})," as it only maps the implementations to the given ",(0,t.jsx)(n.code,{children:"T"})," ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),"."]})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(a.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterTypesAs(new[] \n { \n typeof(DbBackup), \n typeof(ConsoleLogger), \n typeof(StorageCleanup) \n });\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nILogger logger = container.Resolve(); // error, not found\nIJob job = container.Resolve(); // StorageCleanup\nDbBackup backup = container.Resolve(); // error, not found\n"})})}),(0,t.jsx)(a.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterTypesAs(typeof(IJob), new[] \n { \n typeof(DbBackup), \n typeof(ConsoleLogger), \n typeof(StorageCleanup) \n });\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nILogger logger = container.Resolve(); // error, not found\nIJob job = container.Resolve(); // StorageCleanup\nDbBackup backup = container.Resolve(); // error, not found\n"})})})]})})]}),"\n",(0,t.jsx)(n.h2,{id:"assembly-registration",children:"Assembly registration"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["The batch registration API ",(0,t.jsx)(n.em,{children:"(filters, registration configuration action, self-registration)"})," is also usable for registering services from given assemblies."]}),(0,t.jsxs)(n.p,{children:["In this example, we assume that the same three services we used in the ",(0,t.jsx)(n.a,{href:"#batch-registration",children:"batch registration"})," section are in the same assembly."]}),(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["The container also detects and registers open-generic definitions (when applicable) from the supplied type collection. You can read about ",(0,t.jsx)(n.a,{href:"/docs/advanced/generics#open-generics",children:"open-generics here"}),"."]})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Single assembly",label:"Single assembly",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterAssembly(typeof(DbBackup).Assembly,\n // service filter, register to interfaces only\n serviceTypeSelector: (impl, service) => service.IsInterface,\n registerSelf: false,\n configurator: options => options.WithoutDisposalTracking()\n);\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nIEnumerable jobs = container.ResolveAll(); // 0 items\nILogger logger = container.Resolve(); // ConsoleLogger\nDbBackup backup = container.Resolve(); // error, not found\n"})})}),(0,t.jsx)(a.A,{value:"Multiple assemblies",label:"Multiple assemblies",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterAssemblies(new[] \n { \n typeof(DbBackup).Assembly, \n typeof(JobFromAnotherAssembly).Assembly \n },\n // service filter, register to interfaces only\n serviceTypeSelector: (impl, service) => service.IsInterface,\n registerSelf: false,\n configurator: options => options.WithoutDisposalTracking()\n);\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nIEnumerable jobs = container.ResolveAll(); // 0 items\nILogger logger = container.Resolve(); // ConsoleLogger\nDbBackup backup = container.Resolve(); // error, not found\n"})})}),(0,t.jsx)(a.A,{value:"Containing type",label:"Containing type",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterAssemblyContaining(\n // service filter, register to interfaces only\n serviceTypeSelector: (impl, service) => service.IsInterface,\n registerSelf: false,\n configurator: options => options.WithoutDisposalTracking()\n);\n\nIEnumerable jobs = container.ResolveAll(); // 2 items\nIEnumerable jobs = container.ResolveAll(); // 0 items\nILogger logger = container.Resolve(); // ConsoleLogger\nDbBackup backup = container.Resolve(); // error, not found\n"})})})]})})]}),"\n",(0,t.jsx)(n.h2,{id:"composition-root",children:"Composition root"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.a,{href:"https://blog.ploeh.dk/2011/07/28/CompositionRoot/",children:"Composition Root"})," is an entry point where all services required to make a component functional are wired together."]}),(0,t.jsxs)(n.p,{children:["Stashbox provides an ",(0,t.jsx)(n.code,{children:"ICompositionRoot"})," interface that can be used to define an entry point for a given component or even for an entire assembly."]}),(0,t.jsxs)(n.p,{children:["You can wire up your ",(0,t.jsx)(n.em,{children:"composition root"})," implementation with ",(0,t.jsx)(n.code,{children:"ComposeBy()"}),", or you can let the container find and execute all available ",(0,t.jsx)(n.em,{children:"composition root"})," implementations within an assembly."]}),(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["Your ",(0,t.jsx)(n.code,{children:"ICompositionRoot"})," implementation also can have dependencies that the container will resolve."]})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"class ExampleRoot : ICompositionRoot\n{\n public ExampleRoot(IDependency rootDependency)\n { }\n\n public void Compose(IStashboxContainer container)\n {\n container.Register();\n container.Register();\n }\n}\n"})}),(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Single",label:"Single",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// compose a single root.\ncontainer.ComposeBy();\n"})})}),(0,t.jsx)(a.A,{value:"Assembly",label:"Assembly",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// compose every root in the given assembly.\ncontainer.ComposeAssembly(typeof(IServiceA).Assembly);\n"})})}),(0,t.jsx)(a.A,{value:"Override",label:"Override",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"// compose a single root with dependency override.\ncontainer.ComposeBy(new CustomRootDependency());\n"})})})]})]})]}),"\n",(0,t.jsx)(n.h2,{id:"injection-parameters",children:"Injection parameters"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"If you have pre-evaluated dependencies you'd like to inject at resolution time, you can set them as injection parameters during registration."}),(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsx)(n.p,{children:"Injection parameter names are matched to constructor arguments or field/property names."})})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.Register(options => options\n .WithInjectionParameter("logger", new ConsoleLogger())\n .WithInjectionParameter("eventBroadcaster", new MessageBus());\n\n// the injection parameters will be passed to DbBackup\'s constructor.\nIJob backup = container.Resolve();\n'})})})]}),"\n",(0,t.jsx)(n.h2,{id:"initializer--finalizer",children:"Initializer / finalizer"}),"\n",(0,t.jsxs)(i.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"The container provides specific extension points to let you react to lifetime events of an instantiated service."}),(0,t.jsxs)(n.p,{children:["For this reason, you can specify ",(0,t.jsx)(n.em,{children:"Initializer"})," and ",(0,t.jsx)(n.em,{children:"Finalizer"})," delegates. The ",(0,t.jsx)(n.em,{children:"finalizer"})," is called upon the service's ",(0,t.jsx)(n.a,{href:"/docs/guides/scopes#disposal",children:"disposal"}),", and the ",(0,t.jsx)(n.em,{children:"initializer"})," is called upon the service's construction."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(options => options\n // delegate that called right after instantiation.\n .WithInitializer((logger, resolver) => logger.OpenFile())\n // delegate that called right before the instance's disposal.\n .WithFinalizer(logger => logger.CloseFile()));\n"})})})]})]})}function g(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(u,{...e})}):u(e)}},9365:(e,n,s)=>{s.d(n,{A:()=>o});s(6540);var t=s(870);const r={tabItem:"tabItem_Ymn6"};var i=s(4848);function o(e){let{children:n,hidden:s,className:o}=e;return(0,i.jsx)("div",{role:"tabpanel",className:(0,t.A)(r.tabItem,o),hidden:s,children:n})}},1470:(e,n,s)=>{s.d(n,{A:()=>I});var t=s(6540),r=s(870),i=s(3104),o=s(6347),a=s(205),l=s(7485),c=s(1682),d=s(9466);function h(e){return t.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,t.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function p(e){const{values:n,children:s}=e;return(0,t.useMemo)((()=>{const e=n??function(e){return h(e).map((e=>{let{props:{value:n,label:s,attributes:t,default:r}}=e;return{value:n,label:s,attributes:t,default:r}}))}(s);return function(e){const n=(0,c.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,s])}function u(e){let{value:n,tabValues:s}=e;return s.some((e=>e.value===n))}function g(e){let{queryString:n=!1,groupId:s}=e;const r=(0,o.W6)(),i=function(e){let{queryString:n=!1,groupId:s}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!s)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return s??null}({queryString:n,groupId:s});return[(0,l.aZ)(i),(0,t.useCallback)((e=>{if(!i)return;const n=new URLSearchParams(r.location.search);n.set(i,e),r.replace({...r.location,search:n.toString()})}),[i,r])]}function m(e){const{defaultValue:n,queryString:s=!1,groupId:r}=e,i=p(e),[o,l]=(0,t.useState)((()=>function(e){let{defaultValue:n,tabValues:s}=e;if(0===s.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!u({value:n,tabValues:s}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${s.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const t=s.find((e=>e.default))??s[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:i}))),[c,h]=g({queryString:s,groupId:r}),[m,b]=function(e){let{groupId:n}=e;const s=function(e){return e?`docusaurus.tab.${e}`:null}(n),[r,i]=(0,d.Dv)(s);return[r,(0,t.useCallback)((e=>{s&&i.set(e)}),[s,i])]}({groupId:r}),v=(()=>{const e=c??m;return u({value:e,tabValues:i})?e:null})();(0,a.A)((()=>{v&&l(v)}),[v]);return{selectedValue:o,selectValue:(0,t.useCallback)((e=>{if(!u({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);l(e),h(e),b(e)}),[h,b,i]),tabValues:i}}var b=s(2303);const v={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var j=s(4848);function x(e){let{className:n,block:s,selectedValue:t,selectValue:o,tabValues:a}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,i.a_)(),d=e=>{const n=e.currentTarget,s=l.indexOf(n),r=a[s].value;r!==t&&(c(n),o(r))},h=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const s=l.indexOf(e.currentTarget)+1;n=l[s]??l[0];break}case"ArrowLeft":{const s=l.indexOf(e.currentTarget)-1;n=l[s]??l[l.length-1];break}}n?.focus()};return(0,j.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":s},n),children:a.map((e=>{let{value:n,label:s,attributes:i}=e;return(0,j.jsx)("li",{role:"tab",tabIndex:t===n?0:-1,"aria-selected":t===n,ref:e=>l.push(e),onKeyDown:h,onClick:d,...i,className:(0,r.A)("tabs__item",v.tabItem,i?.className,{"tabs__item--active":t===n}),children:s??n},n)}))})}function f(e){let{lazy:n,children:s,selectedValue:r}=e;const i=(Array.isArray(s)?s:[s]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===r));return e?(0,t.cloneElement)(e,{className:"margin-top--md"}):null}return(0,j.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,t.cloneElement)(e,{key:n,hidden:e.props.value!==r})))})}function y(e){const n=m(e);return(0,j.jsxs)("div",{className:(0,r.A)("tabs-container",v.tabList),children:[(0,j.jsx)(x,{...e,...n}),(0,j.jsx)(f,{...e,...n})]})}function I(e){const n=(0,b.A)();return(0,j.jsx)(y,{...e,children:h(e.children)},String(n))}},7470:(e,n,s)=>{s.d(n,{A:()=>o});var t=s(6540);const r={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var i=s(4848);function o(e){let{children:n}=e,s=t.Children.toArray(n).filter((e=>e));return(0,i.jsxs)("div",{className:r.codeDescContainer,children:[(0,i.jsx)("div",{className:r.desc,children:s[0]}),(0,i.jsx)("div",{className:r.example,children:s[1]})]})}},8453:(e,n,s)=>{s.d(n,{R:()=>o,x:()=>a});var t=s(6540);const r={},i=t.createContext(r);function o(e){const n=t.useContext(i);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),t.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/4047545b.9ec870e0.js b/assets/js/4047545b.f98d0351.js similarity index 99% rename from assets/js/4047545b.9ec870e0.js rename to assets/js/4047545b.f98d0351.js index 68999215..f2bfe3ab 100644 --- a/assets/js/4047545b.9ec870e0.js +++ b/assets/js/4047545b.f98d0351.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[414],{685:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>h,contentTitle:()=>a,default:()=>g,frontMatter:()=>l,metadata:()=>d,toc:()=>p});var s=n(4848),t=n(8453),r=n(7470),o=n(1470),c=n(9365);const l={},a="Registration configuration",d={id:"configuration/registration-configuration",title:"Registration configuration",description:"Most of the registration methods have an Action parameter, enabling several customization options on the given registration.",source:"@site/docs/configuration/registration-configuration.md",sourceDirName:"configuration",slug:"/configuration/registration-configuration",permalink:"/stashbox/docs/configuration/registration-configuration",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/configuration/registration-configuration.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Scopes",permalink:"/stashbox/docs/guides/scopes"},next:{title:"Container configuration",permalink:"/stashbox/docs/configuration/container-configuration"}},h={},p=[{value:"General options",id:"general-options",level:2},{value:"WithName",id:"withname",level:3},{value:"WithInstance",id:"withinstance",level:3},{value:"WithoutDisposalTracking",id:"withoutdisposaltracking",level:3},{value:"WithMetadata",id:"withmetadata",level:3},{value:"WithDynamicResolution",id:"withdynamicresolution",level:3},{value:"HasServiceType",id:"hasservicetype",level:3},{value:"Initializer / finalizer",id:"initializer--finalizer",level:2},{value:"WithFinalizer",id:"withfinalizer",level:3},{value:"WithInitializer",id:"withinitializer",level:3},{value:"Replace",id:"replace",level:2},{value:"Multiple services",id:"multiple-services",level:2},{value:"AsImplementedTypes",id:"asimplementedtypes",level:3},{value:"AsServiceAlso",id:"asservicealso",level:3},{value:"Dependency configuration",id:"dependency-configuration",level:2},{value:"Lifetime",id:"lifetime",level:2},{value:"WithSingletonLifetime",id:"withsingletonlifetime",level:3},{value:"WithScopedLifetime",id:"withscopedlifetime",level:3},{value:"WithPerScopedRequestLifetime",id:"withperscopedrequestlifetime",level:3},{value:"WithPerRequestLifetime",id:"withperrequestlifetime",level:3},{value:"WithAutoLifetime",id:"withautolifetime",level:3},{value:"WithLifetime",id:"withlifetime",level:3},{value:"Conditions",id:"conditions",level:2},{value:"WhenHas",id:"whenhas",level:3},{value:"WhenResolutionPathHas",id:"whenresolutionpathhas",level:3},{value:"WhenDependantIs",id:"whendependantis",level:3},{value:"WhenInResolutionPathOf",id:"wheninresolutionpathof",level:3},{value:"When",id:"when",level:3},{value:"Constructor selection",id:"constructor-selection",level:2},{value:"WithConstructorSelectionRule",id:"withconstructorselectionrule",level:3},{value:"PreferMostParameters",id:"prefermostparameters",level:4},{value:"PreferLeastParameters",id:"preferleastparameters",level:4},{value:"Custom",id:"custom",level:4},{value:"WithConstructorByArgumentTypes",id:"withconstructorbyargumenttypes",level:3},{value:"WithConstructorByArguments",id:"withconstructorbyarguments",level:3},{value:"Property / field Injection",id:"property--field-injection",level:2},{value:"WithAutoMemberInjection",id:"withautomemberinjection",level:3},{value:"PropertiesWithPublicSetter",id:"propertieswithpublicsetter",level:4},{value:"PropertiesWithLimitedAccess",id:"propertieswithlimitedaccess",level:4},{value:"PrivateFields",id:"privatefields",level:4},{value:"Combined rules",id:"combined-rules",level:4},{value:"Member selection filter",id:"member-selection-filter",level:4},{value:"Required member injection",id:"required-member-injection",level:2},{value:"Injection parameters",id:"injection-parameters",level:2},{value:"WithInjectionParameters",id:"withinjectionparameters",level:3},{value:"WithInjectionParameter",id:"withinjectionparameter",level:3},{value:"Factory",id:"factory",level:2},{value:"Scope definition",id:"scope-definition",level:2},{value:"InNamedScope",id:"innamedscope",level:3},{value:"InScopeDefinedBy",id:"inscopedefinedby",level:3},{value:"DefinesScope",id:"definesscope",level:3},{value:"Decorator specific",id:"decorator-specific",level:2},{value:"WhenDecoratedServiceIs",id:"whendecoratedserviceis",level:3},{value:"WhenDecoratedServiceHas",id:"whendecoratedservicehas",level:3},{value:"Unknown registration specific",id:"unknown-registration-specific",level:2},{value:"SetImplementationType",id:"setimplementationtype",level:3},{value:"Skip",id:"skip",level:3}];function u(e){const i={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",p:"p",pre:"pre",strong:"strong",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(i.h1,{id:"registration-configuration",children:"Registration configuration"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsxs)(i.p,{children:["Most of the registration methods have an ",(0,s.jsx)(i.code,{children:"Action"})," parameter, enabling several customization options on the given registration."]}),(0,s.jsxs)(i.p,{children:["Here are three examples that show how the API's usage looks like.\nThey cover the exact functionalities you've read about in the ",(0,s.jsx)(i.a,{href:"/docs/guides/basics",children:"basics"})," section but are achieved with the options API."]})]}),(0,s.jsx)("div",{children:(0,s.jsxs)(o.A,{children:[(0,s.jsxs)(c.A,{value:"Named",label:"Named",children:[(0,s.jsx)(i.p,{children:"This is how you can use the options API to set a registration's name:"}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithName("DbBackup"));\n'})})]}),(0,s.jsxs)(c.A,{value:"Lifetime",label:"Lifetime",children:[(0,s.jsxs)(i.p,{children:["It was mentioned in the ",(0,s.jsx)(i.a,{href:"/docs/guides/basics#lifetime-shortcuts",children:"Lifetime shortcuts"})," section, that those methods are only sugars; under the curtain, they are also using this API:"]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithLifetime(Lifetimes.Singleton));\n"})})]}),(0,s.jsxs)(c.A,{value:"Instance",label:"Instance",children:[(0,s.jsx)(i.p,{children:"An example of how you can register an instance with the options API:"}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithInstance(new DbBackup()));\n"})})]})]})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsx)(i.p,{children:"The registration configuration API is fluent, which means all option methods can be chained after each other.\nThis provides an easier way to configure complicated registrations."})}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithName("DbBackup")\n .WithLifetime(Lifetimes.Singleton)\n .WithoutDisposalTracking());\n'})})})]}),"\n",(0,s.jsx)(i.h2,{id:"general-options",children:"General options"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withname",children:(0,s.jsx)(i.code,{children:"WithName"})}),(0,s.jsx)(i.p,{children:"Sets the name identifier of the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(config => config\n .WithName("Console"));\n'})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withinstance",children:(0,s.jsx)(i.code,{children:"WithInstance"})}),(0,s.jsx)(i.p,{children:"Sets an existing instance for the registration."}),(0,s.jsxs)(i.p,{children:["Passing true for the ",(0,s.jsx)(i.code,{children:"wireUp"})," parameter means that the container performs member / method injection on the registered instance."]})]}),(0,s.jsx)("div",{children:(0,s.jsxs)(o.A,{children:[(0,s.jsx)(c.A,{value:"Instance",label:"Instance",children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithInstance(new ConsoleLogger()));\n"})})}),(0,s.jsx)(c.A,{value:"WireUp",label:"WireUp",children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithInstance(new ConsoleLogger(), wireUp: true));\n"})})})]})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withoutdisposaltracking",children:(0,s.jsx)(i.code,{children:"WithoutDisposalTracking"})}),(0,s.jsx)(i.p,{children:"Force disables the disposal tracking on the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithoutDisposalTracking());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withmetadata",children:(0,s.jsx)(i.code,{children:"WithMetadata"})}),(0,s.jsxs)(i.p,{children:["Sets additional metadata for the registration. It's attached to the service upon its resolution through ",(0,s.jsx)(i.code,{children:"ValueTuple<,>"}),", ",(0,s.jsx)(i.code,{children:"Tuple<,>"}),", or ",(0,s.jsx)(i.code,{children:"Metadata<,>"})," wrappers."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithMetadata(connectionString));\n\nvar jobWithConnectionString = container.Resolve>();\nConsole.WriteLine(jobWithConnectionString.Item2); // prints the connection string.\n\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withdynamicresolution",children:(0,s.jsx)(i.code,{children:"WithDynamicResolution"})}),(0,s.jsxs)(i.p,{children:["Indicates that the service's resolution should be handled by a dynamic ",(0,s.jsx)(i.code,{children:"Resolve()"})," call on the current ",(0,s.jsx)(i.code,{children:"IDependencyResolver"})," instead of a pre-built instantiation expression."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register();\ncontainer.Register(options => options\n .WithDynamicResolution());\n\n// new DbBackup(currentScope.Resolve());\nvar job = container.Resolve();\n\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"hasservicetype",children:(0,s.jsx)(i.code,{children:"HasServiceType"})}),(0,s.jsx)(i.p,{children:"Used to build conditions based on service type in batch/assembly registrations.\nIt determines whether the registration is mapped to the given service type."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.RegisterAssemblyContaining(configurator: options =>\n {\n if (options.HasServiceType())\n options.WithScopedLifetime();\n });\n\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"initializer--finalizer",children:"Initializer / finalizer"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withfinalizer",children:(0,s.jsx)(i.code,{children:"WithFinalizer"})}),(0,s.jsx)(i.p,{children:"Sets a custom cleanup delegate that will be invoked when the scope / container holding the instance is being disposed."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithFinalizer(logger => logger\n .CloseFile()));\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withinitializer",children:(0,s.jsx)(i.code,{children:"WithInitializer"})}),(0,s.jsx)(i.p,{children:"Sets a custom initializer delegate that will be invoked when the given service is being instantiated."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithInitializer((logger, resolver) => logger\n .OpenFile()));\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"replace",children:"Replace"}),"\n",(0,s.jsxs)(o.A,{children:[(0,s.jsxs)(c.A,{value:"ReplaceExisting",label:"ReplaceExisting",children:[(0,s.jsxs)(i.p,{children:["Indicates whether the container should replace an existing registration with the current one (based on ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," and name). If there's no existing registration in place, the actual one will be added to the registration list."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .ReplaceExisting());\n"})})]}),(0,s.jsxs)(c.A,{value:"ReplaceOnlyIfExists",label:"ReplaceOnlyIfExists",children:[(0,s.jsxs)(i.p,{children:["The same as ",(0,s.jsx)(i.code,{children:"ReplaceExisting()"})," except that the container will do the replace only when there's an already ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"registered service"})," with the same type or name."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .ReplaceOnlyIfExists());\n"})})]})]}),"\n",(0,s.jsx)(i.h2,{id:"multiple-services",children:"Multiple services"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about binding a registration to multiple services ",(0,s.jsx)(i.a,{href:"/docs/guides/advanced-registration#binding-to-multiple-services",children:"here"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"asimplementedtypes",children:(0,s.jsx)(i.code,{children:"AsImplementedTypes"})}),(0,s.jsx)(i.p,{children:"The service will be mapped to all of its implemented interfaces and base types."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .AsImplementedTypes());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"asservicealso",children:(0,s.jsx)(i.code,{children:"AsServiceAlso"})}),(0,s.jsxs)(i.p,{children:["Binds the currently configured registration to an additional ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),". The registered type must implement or extend the additional ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),"."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .AsServiceAlso()\n // or\n .AsServiceAlso(typeof(IRepository)));\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"dependency-configuration",children:"Dependency configuration"}),"\n",(0,s.jsxs)(i.p,{children:["These options allows the same configuration functionality as the ",(0,s.jsx)(i.a,{href:"/docs/guides/service-resolution#attributes",children:"dependency attribute"}),"."]}),"\n",(0,s.jsxs)(o.A,{children:[(0,s.jsxs)(c.A,{value:"By parameter type",label:"By parameter type",children:[(0,s.jsxs)(i.p,{children:["Binds a constructor / method parameter or a property / field to a named registration by the parameter's type. The container will perform a ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"})," on the bound dependency. The second parameter used to set the name of the dependency."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithDependencyBinding(typeof(ILogger), "FileLogger"));\n'})})]}),(0,s.jsxs)(c.A,{value:"By parameter name",label:"By parameter name",children:[(0,s.jsxs)(i.p,{children:["Binds a constructor / method parameter or a property / field to a named registration by the parameter's name. The container will perform a ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"})," on the bound dependency. The second parameter used to set the name of the dependency."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithDependencyBinding("logger", "FileLogger"));\n'})})]}),(0,s.jsxs)(c.A,{value:"By expression",label:"By expression",children:[(0,s.jsx)(i.p,{children:"Marks a member (property / field) as a dependency that should be filled by the container. The second parameter used to set the name of the dependency."}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithDependencyBinding(logger => logger.Logger, "ConsoleLogger"));\n'})})]})]}),"\n",(0,s.jsx)(i.h2,{id:"lifetime",children:"Lifetime"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about lifetimes ",(0,s.jsx)(i.a,{href:"/docs/guides/lifetimes",children:"here"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withsingletonlifetime",children:(0,s.jsx)(i.code,{children:"WithSingletonLifetime"})}),(0,s.jsx)(i.p,{children:"Sets a singleton lifetime for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n .WithSingletonLifetime());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withscopedlifetime",children:(0,s.jsx)(i.code,{children:"WithScopedLifetime"})}),(0,s.jsx)(i.p,{children:"Sets a scoped lifetime for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n .WithScopedLifetime());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withperscopedrequestlifetime",children:(0,s.jsx)(i.code,{children:"WithPerScopedRequestLifetime"})}),(0,s.jsxs)(i.p,{children:["Sets the lifetime to ",(0,s.jsx)(i.code,{children:"PerScopedRequestLifetime"}),". This lifetime will create a new instance between scoped services. This means that every scoped service will get a different instance but within their dependency tree it will behave as a singleton."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithPerScopedRequestLifetime());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withperrequestlifetime",children:(0,s.jsx)(i.code,{children:"WithPerRequestLifetime"})}),(0,s.jsxs)(i.p,{children:["Sets the lifetime to ",(0,s.jsx)(i.code,{children:"PerRequestLifetime"}),". This lifetime will create a new instance between resolution requests. Within the request the same instance will be re-used."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithPerRequestLifetime());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withautolifetime",children:(0,s.jsx)(i.code,{children:"WithAutoLifetime"})}),(0,s.jsx)(i.p,{children:"Sets the lifetime to auto lifetime. This lifetime aligns to the lifetime of the resolved service's dependencies. When the underlying service has a dependency with a higher lifespan, this lifetime will inherit that lifespan up to a given boundary."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withlifetime",children:(0,s.jsx)(i.code,{children:"WithLifetime"})}),(0,s.jsx)(i.p,{children:"Sets a custom lifetime for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n .WithLifetime(new CustomLifetime()));\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"conditions",children:"Conditions"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about the concept of conditional resolution ",(0,s.jsx)(i.a,{href:"/docs/guides/service-resolution#conditional-resolution",children:"here"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"whenhas",children:(0,s.jsx)(i.code,{children:"WhenHas"})}),(0,s.jsx)(i.p,{children:"Sets an attribute condition for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n .WhenHas());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"whenresolutionpathhas",children:(0,s.jsx)(i.code,{children:"WhenResolutionPathHas"})}),(0,s.jsx)(i.p,{children:"Sets a resolution path condition for the registration. The service will be selected only in the resolution path of the target that has the given attribute.\nThis means that only the direct and sub-dependencies of the target type that has the given attribute will get the configured service."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n // Each direct and sub-dependency of any service that has\n // a ConsoleAttribute will get FileLogger wherever they \n // depend on ILogger. \n .WhenResolutionPathHas());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"whendependantis",children:(0,s.jsx)(i.code,{children:"WhenDependantIs"})}),(0,s.jsx)(i.p,{children:"Sets a parent target condition for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n .WhenDependantIs());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"wheninresolutionpathof",children:(0,s.jsx)(i.code,{children:"WhenInResolutionPathOf"})}),(0,s.jsx)(i.p,{children:"Sets a resolution path condition for the registration. The service will be selected only in the resolution path of the given target.\nThis means that only the direct and sub-dependencies of the target type will get the configured service."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n // Each direct and sub-dependency of UserRepository\n // will get FileLogger wherever they depend on ILogger. \n .WhenInResolutionPathOf());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"when",children:(0,s.jsx)(i.code,{children:"When"})}),(0,s.jsx)(i.p,{children:"Sets a custom user-defined condition for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n .When(typeInfo => typeInfo.ParentType == typeof(UserRepository)));\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"constructor-selection",children:"Constructor selection"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withconstructorselectionrule",children:(0,s.jsx)(i.code,{children:"WithConstructorSelectionRule"})}),(0,s.jsx)(i.p,{children:"Sets the constructor selection rule for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithConstructorSelectionRule(...));\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"prefermostparameters",children:"PreferMostParameters"}),(0,s.jsx)(i.p,{children:"Selects the constructor which has the longest parameter list."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithConstructorSelectionRule(\n Rules.ConstructorSelection.PreferMostParameters)\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"preferleastparameters",children:"PreferLeastParameters"}),(0,s.jsx)(i.p,{children:"Selects the constructor which has the shortest parameter list."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithConstructorSelectionRule(\n Rules.ConstructorSelection.PreferLeastParameters)\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"custom",children:"Custom"}),(0,s.jsx)(i.p,{children:"You can set your own custom constructor ordering logic."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithConstructorSelectionRule(\n constructors => { /* custom constructor sorting logic */ })\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withconstructorbyargumenttypes",children:(0,s.jsx)(i.code,{children:"WithConstructorByArgumentTypes"})}),(0,s.jsx)(i.p,{children:"Selects a constructor by its argument types."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithConstructorByArgumentTypes(typeof(ILogger)));\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withconstructorbyarguments",children:(0,s.jsx)(i.code,{children:"WithConstructorByArguments"})}),(0,s.jsx)(i.p,{children:"Selects a constructor by its arguments to use during resolution. These arguments are used to invoke the selected constructor."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithConstructorByArguments(new ConsoleLogger()));\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"property--field-injection",children:"Property / field Injection"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withautomemberinjection",children:(0,s.jsx)(i.code,{children:"WithAutoMemberInjection"})}),(0,s.jsx)(i.p,{children:"Enables the auto member injection and sets the rule for it."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithAutoMemberInjection(...));\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"propertieswithpublicsetter",children:"PropertiesWithPublicSetter"}),(0,s.jsx)(i.p,{children:"With this flag, the container will perform auto-injection on properties with a public setter."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithAutoMemberInjection(\n Rules.AutoMemberInjectionRules.PropertiesWithPublicSetter)\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"propertieswithlimitedaccess",children:"PropertiesWithLimitedAccess"}),(0,s.jsx)(i.p,{children:"With this flag, the container will perform auto-injection on properties which has a non-public setter as well."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithAutoMemberInjection(\n Rules.AutoMemberInjectionRules.PropertiesWithLimitedAccess)\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"privatefields",children:"PrivateFields"}),(0,s.jsx)(i.p,{children:"With this flag, the container will perform auto-injection on private fields too."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithAutoMemberInjection(\n Rules.AutoMemberInjectionRules.PrivateFields)\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"combined-rules",children:"Combined rules"}),(0,s.jsxs)(i.p,{children:["As these rules are ",(0,s.jsx)(i.a,{href:"https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/enum#enumeration-types-as-bit-flags",children:"bit flags"}),", you can use them combined together with bitwise logical operators."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithAutoMemberInjection(Rules.AutoMemberInjectionRules.PrivateFields | \n Rules.AutoMemberInjectionRules.PropertiesWithPublicSetter)\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"member-selection-filter",children:"Member selection filter"}),(0,s.jsx)(i.p,{children:"You can pass your own member selection logic to control which members should be auto injected."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithAutoMemberInjection(filter: member => member.Type != typeof(ILogger)));\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"required-member-injection",children:"Required member injection"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsxs)(i.p,{children:["With this option, you can enable or disable the auto injection of members defined with C# 11's ",(0,s.jsx)(i.a,{href:"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required",children:(0,s.jsx)(i.code,{children:"required"})})," keyword."]})}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithRequiredMemberInjection(enabled: false));\n"})})})]}),"\n",(0,s.jsx)(i.admonition,{type:"note",children:(0,s.jsxs)(i.p,{children:["The required member injection option is ",(0,s.jsx)(i.strong,{children:"enabled"})," by default."]})}),"\n",(0,s.jsx)(i.h2,{id:"injection-parameters",children:"Injection parameters"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withinjectionparameters",children:(0,s.jsx)(i.code,{children:"WithInjectionParameters"})}),(0,s.jsx)(i.p,{children:"Sets multiple injection parameters for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithInjectionParameters(new KeyValuePair("logger", new ConsoleLogger()));\n'})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withinjectionparameter",children:(0,s.jsx)(i.code,{children:"WithInjectionParameter"})}),(0,s.jsx)(i.p,{children:"Sets a single injection parameter for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithInjectionParameter("logger", new ConsoleLogger());\n'})})})]}),"\n",(0,s.jsx)(i.h2,{id:"factory",children:"Factory"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about the concept of factory registration ",(0,s.jsx)(i.a,{href:"/docs/guides/advanced-registration?id=factory-registration",children:"here"}),"."]}),"\n",(0,s.jsxs)(o.A,{children:[(0,s.jsxs)(c.A,{value:"Parameterized",label:"Parameterized",children:[(0,s.jsxs)(i.p,{children:[(0,s.jsx)(i.strong,{children:"WithFactory"})," - Sets a factory delegate that could take various number of pre-resolved dependencies as parameters and returns the service instance."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"// 1 parameter factory\ncontainer.Register(options => options\n .WithFactory(logger => new UserRepository(logger));\n\n// 2 parameters factory\ncontainer.Register(options => options\n .WithFactory((logger, context) => new UserRepository(logger, context));\n\n// 3 parameters factory\ncontainer.Register(options => options\n .WithFactory((logger, context, options) => \n new UserRepository(logger, context, options));\n\n// 4 parameters factory\ncontainer.Register(options => options\n .WithFactory((logger, connection, options, validator) => \n new UserRepository(logger, connection, options, validator));\n\n// 5 parameters factory\ncontainer.Register(options => options\n .WithFactory(\n (logger, connection, options, validator, permissionManager) => \n new UserRepository(logger, connection, options, validator, permissionManager));\n"})}),(0,s.jsxs)(i.p,{children:["You can also get the current ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#dependency-resolver",children:"dependency resolver"})," as a pre-resolved parameter:"]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithFactory((logger, resolver) => \n new UserRepository(logger, resolver.Resolve())));\n"})})]}),(0,s.jsxs)(c.A,{value:"Parameter-less",label:"Parameter-less",children:[(0,s.jsxs)(i.p,{children:[(0,s.jsx)(i.strong,{children:"WithFactory"})," - Sets a parameter-less factory delegate that returns the service instance."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithFactory(()) => new UserRepository(new ConsoleLogger()));\n"})})]}),(0,s.jsxs)(c.A,{value:"Resolver parameter",label:"Resolver parameter",children:[(0,s.jsxs)(i.p,{children:[(0,s.jsx)(i.strong,{children:"WithFactory"})," - Sets a factory delegate that takes an ",(0,s.jsx)(i.code,{children:"IDependencyResolver"})," as parameter and returns the service instance."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithFactory(resolver => new UserRepository(resolver.Resolve()));\n"})})]})]}),"\n",(0,s.jsx)(i.admonition,{type:"info",children:(0,s.jsxs)(i.p,{children:["All factory configuration method has an ",(0,s.jsx)(i.code,{children:"isCompiledLambda"})," parameter which should be set to ",(0,s.jsx)(i.code,{children:"true"})," if the passed delegate is compiled from an ",(0,s.jsx)(i.code,{children:"Expression"})," tree."]})}),"\n",(0,s.jsx)(i.h2,{id:"scope-definition",children:"Scope definition"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about the concept of defined scopes ",(0,s.jsx)(i.a,{href:"/docs/guides/scopes?id=service-as-scope",children:"here"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"innamedscope",children:(0,s.jsx)(i.code,{children:"InNamedScope"})}),(0,s.jsx)(i.p,{children:"Sets a scope name condition for the registration; it will be used only when a scope with the same name requests it."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .InNamedScope("UserRepo"));\n'})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"inscopedefinedby",children:(0,s.jsx)(i.code,{children:"InScopeDefinedBy"})}),(0,s.jsx)(i.p,{children:"Sets a condition for the registration; it will be used only within the scope defined by the given type."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .InScopeDefinedBy());\ncontainer.Register(options => options\n .DefinesScope());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"definesscope",children:(0,s.jsx)(i.code,{children:"DefinesScope"})}),(0,s.jsxs)(i.p,{children:["This registration is used as a logical scope for it's dependencies. Dependencies registered with ",(0,s.jsx)(i.code,{children:"InNamedScope()"})," with the same name are preferred during resolution.\nWhen the ",(0,s.jsx)(i.code,{children:"name"})," is not set, the ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," is used as the name. Dependencies registered with ",(0,s.jsx)(i.code,{children:"InScopeDefinedBy()"})," are selected."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .DefinesScope("UserRepo"));\n\n// or\ncontainer.Register(options => options\n .DefinesScope());\n'})})})]}),"\n",(0,s.jsx)(i.h2,{id:"decorator-specific",children:"Decorator specific"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about decorators ",(0,s.jsx)(i.a,{href:"/docs/advanced/decorators",children:"here"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"whendecoratedserviceis",children:(0,s.jsx)(i.code,{children:"WhenDecoratedServiceIs"})}),(0,s.jsx)(i.p,{children:"Sets a decorated target condition for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.RegisterDecorator(options => options\n .WhenDecoratedServiceIs());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"whendecoratedservicehas",children:(0,s.jsx)(i.code,{children:"WhenDecoratedServiceHas"})}),(0,s.jsx)(i.p,{children:"Sets an attribute condition that the decorated target has to satisfy."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.RegisterDecorator(options => options\n .WhenDecoratedServiceHas());\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"unknown-registration-specific",children:"Unknown registration specific"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about unknown type resolution ",(0,s.jsx)(i.a,{href:"/docs/advanced/special-resolution-cases#unknown-type-resolution",children:"here"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"setimplementationtype",children:(0,s.jsx)(i.code,{children:"SetImplementationType"})}),(0,s.jsxs)(i.p,{children:["Sets the current registration's ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"}),"."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"var container = new StashboxContainer(c => c.WithUnknownTypeResolution(config =>\n{\n if (config.ServiceType == typeof(IService))\n config.SetImplementationType(typeof(Service));\n}));\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"skip",children:(0,s.jsx)(i.code,{children:"Skip"})}),(0,s.jsx)(i.p,{children:"Marks the current unknown type registration as skipped."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"var container = new StashboxContainer(c => c.WithUnknownTypeResolution(config =>\n{\n if (config.ServiceType == typeof(IService))\n config.Skip();\n}));\n"})})})]})]})}function g(e={}){const{wrapper:i}={...(0,t.R)(),...e.components};return i?(0,s.jsx)(i,{...e,children:(0,s.jsx)(u,{...e})}):u(e)}},9365:(e,i,n)=>{n.d(i,{A:()=>o});n(6540);var s=n(870);const t={tabItem:"tabItem_Ymn6"};var r=n(4848);function o(e){let{children:i,hidden:n,className:o}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,s.A)(t.tabItem,o),hidden:n,children:i})}},1470:(e,i,n)=>{n.d(i,{A:()=>I});var s=n(6540),t=n(870),r=n(3104),o=n(6347),c=n(205),l=n(7485),a=n(1682),d=n(9466);function h(e){return s.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,s.isValidElement)(e)&&function(e){const{props:i}=e;return!!i&&"object"==typeof i&&"value"in i}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function p(e){const{values:i,children:n}=e;return(0,s.useMemo)((()=>{const e=i??function(e){return h(e).map((e=>{let{props:{value:i,label:n,attributes:s,default:t}}=e;return{value:i,label:n,attributes:s,default:t}}))}(n);return function(e){const i=(0,a.X)(e,((e,i)=>e.value===i.value));if(i.length>0)throw new Error(`Docusaurus error: Duplicate values "${i.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[i,n])}function u(e){let{value:i,tabValues:n}=e;return n.some((e=>e.value===i))}function g(e){let{queryString:i=!1,groupId:n}=e;const t=(0,o.W6)(),r=function(e){let{queryString:i=!1,groupId:n}=e;if("string"==typeof i)return i;if(!1===i)return null;if(!0===i&&!n)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:i,groupId:n});return[(0,l.aZ)(r),(0,s.useCallback)((e=>{if(!r)return;const i=new URLSearchParams(t.location.search);i.set(r,e),t.replace({...t.location,search:i.toString()})}),[r,t])]}function j(e){const{defaultValue:i,queryString:n=!1,groupId:t}=e,r=p(e),[o,l]=(0,s.useState)((()=>function(e){let{defaultValue:i,tabValues:n}=e;if(0===n.length)throw new Error("Docusaurus error: the component requires at least one children component");if(i){if(!u({value:i,tabValues:n}))throw new Error(`Docusaurus error: The has a defaultValue "${i}" but none of its children has the corresponding value. Available values are: ${n.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return i}const s=n.find((e=>e.default))??n[0];if(!s)throw new Error("Unexpected error: 0 tabValues");return s.value}({defaultValue:i,tabValues:r}))),[a,h]=g({queryString:n,groupId:t}),[j,x]=function(e){let{groupId:i}=e;const n=function(e){return e?`docusaurus.tab.${e}`:null}(i),[t,r]=(0,d.Dv)(n);return[t,(0,s.useCallback)((e=>{n&&r.set(e)}),[n,r])]}({groupId:t}),m=(()=>{const e=a??j;return u({value:e,tabValues:r})?e:null})();(0,c.A)((()=>{m&&l(m)}),[m]);return{selectedValue:o,selectValue:(0,s.useCallback)((e=>{if(!u({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);l(e),h(e),x(e)}),[h,x,r]),tabValues:r}}var x=n(2303);const m={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var v=n(4848);function f(e){let{className:i,block:n,selectedValue:s,selectValue:o,tabValues:c}=e;const l=[],{blockElementScrollPositionUntilNextRender:a}=(0,r.a_)(),d=e=>{const i=e.currentTarget,n=l.indexOf(i),t=c[n].value;t!==s&&(a(i),o(t))},h=e=>{let i=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const n=l.indexOf(e.currentTarget)+1;i=l[n]??l[0];break}case"ArrowLeft":{const n=l.indexOf(e.currentTarget)-1;i=l[n]??l[l.length-1];break}}i?.focus()};return(0,v.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,t.A)("tabs",{"tabs--block":n},i),children:c.map((e=>{let{value:i,label:n,attributes:r}=e;return(0,v.jsx)("li",{role:"tab",tabIndex:s===i?0:-1,"aria-selected":s===i,ref:e=>l.push(e),onKeyDown:h,onClick:d,...r,className:(0,t.A)("tabs__item",m.tabItem,r?.className,{"tabs__item--active":s===i}),children:n??i},i)}))})}function y(e){let{lazy:i,children:n,selectedValue:t}=e;const r=(Array.isArray(n)?n:[n]).filter(Boolean);if(i){const e=r.find((e=>e.props.value===t));return e?(0,s.cloneElement)(e,{className:"margin-top--md"}):null}return(0,v.jsx)("div",{className:"margin-top--md",children:r.map(((e,i)=>(0,s.cloneElement)(e,{key:i,hidden:e.props.value!==t})))})}function b(e){const i=j(e);return(0,v.jsxs)("div",{className:(0,t.A)("tabs-container",m.tabList),children:[(0,v.jsx)(f,{...e,...i}),(0,v.jsx)(y,{...e,...i})]})}function I(e){const i=(0,x.A)();return(0,v.jsx)(b,{...e,children:h(e.children)},String(i))}},7470:(e,i,n)=>{n.d(i,{A:()=>o});var s=n(6540);const t={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var r=n(4848);function o(e){let{children:i}=e,n=s.Children.toArray(i).filter((e=>e));return(0,r.jsxs)("div",{className:t.codeDescContainer,children:[(0,r.jsx)("div",{className:t.desc,children:n[0]}),(0,r.jsx)("div",{className:t.example,children:n[1]})]})}},8453:(e,i,n)=>{n.d(i,{R:()=>o,x:()=>c});var s=n(6540);const t={},r=s.createContext(t);function o(e){const i=s.useContext(r);return s.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function c(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:o(e.components),s.createElement(r.Provider,{value:i},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[414],{685:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>h,contentTitle:()=>a,default:()=>g,frontMatter:()=>l,metadata:()=>d,toc:()=>p});var s=n(4848),t=n(8453),r=n(7470),o=n(1470),c=n(9365);const l={},a="Registration configuration",d={id:"configuration/registration-configuration",title:"Registration configuration",description:"Most of the registration methods have an Action parameter, enabling several customization options on the given registration.",source:"@site/docs/configuration/registration-configuration.md",sourceDirName:"configuration",slug:"/configuration/registration-configuration",permalink:"/stashbox/docs/configuration/registration-configuration",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/configuration/registration-configuration.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Scopes",permalink:"/stashbox/docs/guides/scopes"},next:{title:"Container configuration",permalink:"/stashbox/docs/configuration/container-configuration"}},h={},p=[{value:"General options",id:"general-options",level:2},{value:"WithName",id:"withname",level:3},{value:"WithInstance",id:"withinstance",level:3},{value:"WithoutDisposalTracking",id:"withoutdisposaltracking",level:3},{value:"WithMetadata",id:"withmetadata",level:3},{value:"WithDynamicResolution",id:"withdynamicresolution",level:3},{value:"HasServiceType",id:"hasservicetype",level:3},{value:"Initializer / finalizer",id:"initializer--finalizer",level:2},{value:"WithFinalizer",id:"withfinalizer",level:3},{value:"WithInitializer",id:"withinitializer",level:3},{value:"Replace",id:"replace",level:2},{value:"Multiple services",id:"multiple-services",level:2},{value:"AsImplementedTypes",id:"asimplementedtypes",level:3},{value:"AsServiceAlso",id:"asservicealso",level:3},{value:"Dependency configuration",id:"dependency-configuration",level:2},{value:"Lifetime",id:"lifetime",level:2},{value:"WithSingletonLifetime",id:"withsingletonlifetime",level:3},{value:"WithScopedLifetime",id:"withscopedlifetime",level:3},{value:"WithPerScopedRequestLifetime",id:"withperscopedrequestlifetime",level:3},{value:"WithPerRequestLifetime",id:"withperrequestlifetime",level:3},{value:"WithAutoLifetime",id:"withautolifetime",level:3},{value:"WithLifetime",id:"withlifetime",level:3},{value:"Conditions",id:"conditions",level:2},{value:"WhenHas",id:"whenhas",level:3},{value:"WhenResolutionPathHas",id:"whenresolutionpathhas",level:3},{value:"WhenDependantIs",id:"whendependantis",level:3},{value:"WhenInResolutionPathOf",id:"wheninresolutionpathof",level:3},{value:"When",id:"when",level:3},{value:"Constructor selection",id:"constructor-selection",level:2},{value:"WithConstructorSelectionRule",id:"withconstructorselectionrule",level:3},{value:"PreferMostParameters",id:"prefermostparameters",level:4},{value:"PreferLeastParameters",id:"preferleastparameters",level:4},{value:"Custom",id:"custom",level:4},{value:"WithConstructorByArgumentTypes",id:"withconstructorbyargumenttypes",level:3},{value:"WithConstructorByArguments",id:"withconstructorbyarguments",level:3},{value:"Property / field Injection",id:"property--field-injection",level:2},{value:"WithAutoMemberInjection",id:"withautomemberinjection",level:3},{value:"PropertiesWithPublicSetter",id:"propertieswithpublicsetter",level:4},{value:"PropertiesWithLimitedAccess",id:"propertieswithlimitedaccess",level:4},{value:"PrivateFields",id:"privatefields",level:4},{value:"Combined rules",id:"combined-rules",level:4},{value:"Member selection filter",id:"member-selection-filter",level:4},{value:"Required member injection",id:"required-member-injection",level:2},{value:"Injection parameters",id:"injection-parameters",level:2},{value:"WithInjectionParameters",id:"withinjectionparameters",level:3},{value:"WithInjectionParameter",id:"withinjectionparameter",level:3},{value:"Factory",id:"factory",level:2},{value:"Scope definition",id:"scope-definition",level:2},{value:"InNamedScope",id:"innamedscope",level:3},{value:"InScopeDefinedBy",id:"inscopedefinedby",level:3},{value:"DefinesScope",id:"definesscope",level:3},{value:"Decorator specific",id:"decorator-specific",level:2},{value:"WhenDecoratedServiceIs",id:"whendecoratedserviceis",level:3},{value:"WhenDecoratedServiceHas",id:"whendecoratedservicehas",level:3},{value:"Unknown registration specific",id:"unknown-registration-specific",level:2},{value:"SetImplementationType",id:"setimplementationtype",level:3},{value:"Skip",id:"skip",level:3}];function u(e){const i={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",p:"p",pre:"pre",strong:"strong",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(i.h1,{id:"registration-configuration",children:"Registration configuration"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsxs)(i.p,{children:["Most of the registration methods have an ",(0,s.jsx)(i.code,{children:"Action"})," parameter, enabling several customization options on the given registration."]}),(0,s.jsxs)(i.p,{children:["Here are three examples that show how the API's usage looks like.\nThey cover the exact functionalities you've read about in the ",(0,s.jsx)(i.a,{href:"/docs/guides/basics",children:"basics"})," section but are achieved with the options API."]})]}),(0,s.jsx)("div",{children:(0,s.jsxs)(o.A,{children:[(0,s.jsxs)(c.A,{value:"Named",label:"Named",children:[(0,s.jsx)(i.p,{children:"This is how you can use the options API to set a registration's name:"}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithName("DbBackup"));\n'})})]}),(0,s.jsxs)(c.A,{value:"Lifetime",label:"Lifetime",children:[(0,s.jsxs)(i.p,{children:["It was mentioned in the ",(0,s.jsx)(i.a,{href:"/docs/guides/basics#lifetime-shortcuts",children:"Lifetime shortcuts"})," section, that those methods are only sugars; under the curtain, they are also using this API:"]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithLifetime(Lifetimes.Singleton));\n"})})]}),(0,s.jsxs)(c.A,{value:"Instance",label:"Instance",children:[(0,s.jsx)(i.p,{children:"An example of how you can register an instance with the options API:"}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithInstance(new DbBackup()));\n"})})]})]})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsx)(i.p,{children:"The registration configuration API is fluent, which means all option methods can be chained after each other.\nThis provides an easier way to configure complicated registrations."})}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithName("DbBackup")\n .WithLifetime(Lifetimes.Singleton)\n .WithoutDisposalTracking());\n'})})})]}),"\n",(0,s.jsx)(i.h2,{id:"general-options",children:"General options"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withname",children:(0,s.jsx)(i.code,{children:"WithName"})}),(0,s.jsx)(i.p,{children:"Sets the name identifier of the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(config => config\n .WithName("Console"));\n'})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withinstance",children:(0,s.jsx)(i.code,{children:"WithInstance"})}),(0,s.jsx)(i.p,{children:"Sets an existing instance for the registration."}),(0,s.jsxs)(i.p,{children:["Passing true for the ",(0,s.jsx)(i.code,{children:"wireUp"})," parameter means that the container performs member / method injection on the registered instance."]})]}),(0,s.jsx)("div",{children:(0,s.jsxs)(o.A,{children:[(0,s.jsx)(c.A,{value:"Instance",label:"Instance",children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithInstance(new ConsoleLogger()));\n"})})}),(0,s.jsx)(c.A,{value:"WireUp",label:"WireUp",children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithInstance(new ConsoleLogger(), wireUp: true));\n"})})})]})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withoutdisposaltracking",children:(0,s.jsx)(i.code,{children:"WithoutDisposalTracking"})}),(0,s.jsx)(i.p,{children:"Force disables the disposal tracking on the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithoutDisposalTracking());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withmetadata",children:(0,s.jsx)(i.code,{children:"WithMetadata"})}),(0,s.jsxs)(i.p,{children:["Sets additional metadata for the registration. It's attached to the service upon its resolution through ",(0,s.jsx)(i.code,{children:"ValueTuple<,>"}),", ",(0,s.jsx)(i.code,{children:"Tuple<,>"}),", or ",(0,s.jsx)(i.code,{children:"Metadata<,>"})," wrappers."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithMetadata(connectionString));\n\nvar jobWithConnectionString = container.Resolve>();\nConsole.WriteLine(jobWithConnectionString.Item2); // prints the connection string.\n\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withdynamicresolution",children:(0,s.jsx)(i.code,{children:"WithDynamicResolution"})}),(0,s.jsxs)(i.p,{children:["Indicates that the service's resolution should be handled by a dynamic ",(0,s.jsx)(i.code,{children:"Resolve()"})," call on the current ",(0,s.jsx)(i.code,{children:"IDependencyResolver"})," instead of a pre-built instantiation expression."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register();\ncontainer.Register(options => options\n .WithDynamicResolution());\n\n// new DbBackup(currentScope.Resolve());\nvar job = container.Resolve();\n\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"hasservicetype",children:(0,s.jsx)(i.code,{children:"HasServiceType"})}),(0,s.jsx)(i.p,{children:"Used to build conditions based on service type in batch/assembly registrations.\nIt determines whether the registration is mapped to the given service type."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.RegisterAssemblyContaining(configurator: options =>\n {\n if (options.HasServiceType())\n options.WithScopedLifetime();\n });\n\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"initializer--finalizer",children:"Initializer / finalizer"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withfinalizer",children:(0,s.jsx)(i.code,{children:"WithFinalizer"})}),(0,s.jsx)(i.p,{children:"Sets a custom cleanup delegate that will be invoked when the scope / container holding the instance is being disposed."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithFinalizer(logger => logger\n .CloseFile()));\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withinitializer",children:(0,s.jsx)(i.code,{children:"WithInitializer"})}),(0,s.jsx)(i.p,{children:"Sets a custom initializer delegate that will be invoked when the given service is being instantiated."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithInitializer((logger, resolver) => logger\n .OpenFile()));\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"replace",children:"Replace"}),"\n",(0,s.jsxs)(o.A,{children:[(0,s.jsxs)(c.A,{value:"ReplaceExisting",label:"ReplaceExisting",children:[(0,s.jsxs)(i.p,{children:["Indicates whether the container should replace an existing registration with the current one (based on ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," and name). If there's no existing registration in place, the actual one will be added to the registration list."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .ReplaceExisting());\n"})})]}),(0,s.jsxs)(c.A,{value:"ReplaceOnlyIfExists",label:"ReplaceOnlyIfExists",children:[(0,s.jsxs)(i.p,{children:["The same as ",(0,s.jsx)(i.code,{children:"ReplaceExisting()"})," except that the container will do the replace only when there's an already ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"registered service"})," with the same type or name."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .ReplaceOnlyIfExists());\n"})})]})]}),"\n",(0,s.jsx)(i.h2,{id:"multiple-services",children:"Multiple services"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about binding a registration to multiple services ",(0,s.jsx)(i.a,{href:"/docs/guides/advanced-registration#binding-to-multiple-services",children:"here"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"asimplementedtypes",children:(0,s.jsx)(i.code,{children:"AsImplementedTypes"})}),(0,s.jsx)(i.p,{children:"The service will be mapped to all of its implemented interfaces and base types."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .AsImplementedTypes());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"asservicealso",children:(0,s.jsx)(i.code,{children:"AsServiceAlso"})}),(0,s.jsxs)(i.p,{children:["Binds the currently configured registration to an additional ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),". The registered type must implement or extend the additional ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),"."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .AsServiceAlso()\n // or\n .AsServiceAlso(typeof(IRepository)));\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"dependency-configuration",children:"Dependency configuration"}),"\n",(0,s.jsxs)(i.p,{children:["These options allows the same configuration functionality as the ",(0,s.jsx)(i.a,{href:"/docs/guides/service-resolution#attributes",children:"dependency attribute"}),"."]}),"\n",(0,s.jsxs)(o.A,{children:[(0,s.jsxs)(c.A,{value:"By parameter type",label:"By parameter type",children:[(0,s.jsxs)(i.p,{children:["Binds a constructor / method parameter or a property / field to a named registration by the parameter's type. The container will perform a ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"})," on the bound dependency. The second parameter used to set the name of the dependency."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithDependencyBinding(typeof(ILogger), "FileLogger"));\n'})})]}),(0,s.jsxs)(c.A,{value:"By parameter name",label:"By parameter name",children:[(0,s.jsxs)(i.p,{children:["Binds a constructor / method parameter or a property / field to a named registration by the parameter's name. The container will perform a ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"})," on the bound dependency. The second parameter used to set the name of the dependency."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithDependencyBinding("logger", "FileLogger"));\n'})})]}),(0,s.jsxs)(c.A,{value:"By expression",label:"By expression",children:[(0,s.jsx)(i.p,{children:"Marks a member (property / field) as a dependency that should be filled by the container. The second parameter used to set the name of the dependency."}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithDependencyBinding(logger => logger.Logger, "ConsoleLogger"));\n'})})]})]}),"\n",(0,s.jsx)(i.h2,{id:"lifetime",children:"Lifetime"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about lifetimes ",(0,s.jsx)(i.a,{href:"/docs/guides/lifetimes",children:"here"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withsingletonlifetime",children:(0,s.jsx)(i.code,{children:"WithSingletonLifetime"})}),(0,s.jsx)(i.p,{children:"Sets a singleton lifetime for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n .WithSingletonLifetime());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withscopedlifetime",children:(0,s.jsx)(i.code,{children:"WithScopedLifetime"})}),(0,s.jsx)(i.p,{children:"Sets a scoped lifetime for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n .WithScopedLifetime());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withperscopedrequestlifetime",children:(0,s.jsx)(i.code,{children:"WithPerScopedRequestLifetime"})}),(0,s.jsxs)(i.p,{children:["Sets the lifetime to ",(0,s.jsx)(i.code,{children:"PerScopedRequestLifetime"}),". This lifetime will create a new instance between scoped services. This means that every scoped service will get a different instance but within their dependency tree it will behave as a singleton."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithPerScopedRequestLifetime());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withperrequestlifetime",children:(0,s.jsx)(i.code,{children:"WithPerRequestLifetime"})}),(0,s.jsxs)(i.p,{children:["Sets the lifetime to ",(0,s.jsx)(i.code,{children:"PerRequestLifetime"}),". This lifetime will create a new instance between resolution requests. Within the request the same instance will be re-used."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithPerRequestLifetime());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withautolifetime",children:(0,s.jsx)(i.code,{children:"WithAutoLifetime"})}),(0,s.jsx)(i.p,{children:"Sets the lifetime to auto lifetime. This lifetime aligns to the lifetime of the resolved service's dependencies. When the underlying service has a dependency with a higher lifespan, this lifetime will inherit that lifespan up to a given boundary."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withlifetime",children:(0,s.jsx)(i.code,{children:"WithLifetime"})}),(0,s.jsx)(i.p,{children:"Sets a custom lifetime for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n .WithLifetime(new CustomLifetime()));\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"conditions",children:"Conditions"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about the concept of conditional resolution ",(0,s.jsx)(i.a,{href:"/docs/guides/service-resolution#conditional-resolution",children:"here"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"whenhas",children:(0,s.jsx)(i.code,{children:"WhenHas"})}),(0,s.jsx)(i.p,{children:"Sets an attribute condition for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n .WhenHas());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"whenresolutionpathhas",children:(0,s.jsx)(i.code,{children:"WhenResolutionPathHas"})}),(0,s.jsx)(i.p,{children:"Sets a resolution path condition for the registration. The service will be selected only in the resolution path of the target that has the given attribute.\nThis means that only the direct and sub-dependencies of the target type that has the given attribute will get the configured service."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n // Each direct and sub-dependency of any service that has\n // a ConsoleAttribute will get FileLogger wherever they \n // depend on ILogger. \n .WhenResolutionPathHas());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"whendependantis",children:(0,s.jsx)(i.code,{children:"WhenDependantIs"})}),(0,s.jsx)(i.p,{children:"Sets a parent target condition for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n .WhenDependantIs());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"wheninresolutionpathof",children:(0,s.jsx)(i.code,{children:"WhenInResolutionPathOf"})}),(0,s.jsx)(i.p,{children:"Sets a resolution path condition for the registration. The service will be selected only in the resolution path of the given target.\nThis means that only the direct and sub-dependencies of the target type will get the configured service."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n // Each direct and sub-dependency of UserRepository\n // will get FileLogger wherever they depend on ILogger. \n .WhenInResolutionPathOf());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"when",children:(0,s.jsx)(i.code,{children:"When"})}),(0,s.jsx)(i.p,{children:"Sets a custom user-defined condition for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(config => config\n .When(typeInfo => typeInfo.ParentType == typeof(UserRepository)));\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"constructor-selection",children:"Constructor selection"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withconstructorselectionrule",children:(0,s.jsx)(i.code,{children:"WithConstructorSelectionRule"})}),(0,s.jsx)(i.p,{children:"Sets the constructor selection rule for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithConstructorSelectionRule(...));\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"prefermostparameters",children:"PreferMostParameters"}),(0,s.jsx)(i.p,{children:"Selects the constructor which has the longest parameter list."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithConstructorSelectionRule(\n Rules.ConstructorSelection.PreferMostParameters)\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"preferleastparameters",children:"PreferLeastParameters"}),(0,s.jsx)(i.p,{children:"Selects the constructor which has the shortest parameter list."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithConstructorSelectionRule(\n Rules.ConstructorSelection.PreferLeastParameters)\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"custom",children:"Custom"}),(0,s.jsx)(i.p,{children:"You can set your own custom constructor ordering logic."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithConstructorSelectionRule(\n constructors => { /* custom constructor sorting logic */ })\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withconstructorbyargumenttypes",children:(0,s.jsx)(i.code,{children:"WithConstructorByArgumentTypes"})}),(0,s.jsx)(i.p,{children:"Selects a constructor by its argument types."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithConstructorByArgumentTypes(typeof(ILogger)));\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withconstructorbyarguments",children:(0,s.jsx)(i.code,{children:"WithConstructorByArguments"})}),(0,s.jsx)(i.p,{children:"Selects a constructor by its arguments to use during resolution. These arguments are used to invoke the selected constructor."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithConstructorByArguments(new ConsoleLogger()));\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"property--field-injection",children:"Property / field Injection"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withautomemberinjection",children:(0,s.jsx)(i.code,{children:"WithAutoMemberInjection"})}),(0,s.jsx)(i.p,{children:"Enables the auto member injection and sets the rule for it."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithAutoMemberInjection(...));\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"propertieswithpublicsetter",children:"PropertiesWithPublicSetter"}),(0,s.jsx)(i.p,{children:"With this flag, the container will perform auto-injection on properties with a public setter."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithAutoMemberInjection(\n Rules.AutoMemberInjectionRules.PropertiesWithPublicSetter)\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"propertieswithlimitedaccess",children:"PropertiesWithLimitedAccess"}),(0,s.jsx)(i.p,{children:"With this flag, the container will perform auto-injection on properties which has a non-public setter as well."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithAutoMemberInjection(\n Rules.AutoMemberInjectionRules.PropertiesWithLimitedAccess)\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"privatefields",children:"PrivateFields"}),(0,s.jsx)(i.p,{children:"With this flag, the container will perform auto-injection on private fields too."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithAutoMemberInjection(\n Rules.AutoMemberInjectionRules.PrivateFields)\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"combined-rules",children:"Combined rules"}),(0,s.jsxs)(i.p,{children:["As these rules are ",(0,s.jsx)(i.a,{href:"https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/enum#enumeration-types-as-bit-flags",children:"bit flags"}),", you can use them combined together with bitwise logical operators."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"options.WithAutoMemberInjection(Rules.AutoMemberInjectionRules.PrivateFields | \n Rules.AutoMemberInjectionRules.PropertiesWithPublicSetter)\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h4,{id:"member-selection-filter",children:"Member selection filter"}),(0,s.jsx)(i.p,{children:"You can pass your own member selection logic to control which members should be auto injected."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithAutoMemberInjection(filter: member => member.Type != typeof(ILogger)));\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"required-member-injection",children:"Required member injection"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsxs)(i.p,{children:["With this option, you can enable or disable the auto injection of members defined with C# 11's ",(0,s.jsx)(i.a,{href:"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required",children:(0,s.jsx)(i.code,{children:"required"})})," keyword."]})}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithRequiredMemberInjection(enabled: false));\n"})})})]}),"\n",(0,s.jsx)(i.admonition,{type:"note",children:(0,s.jsxs)(i.p,{children:["The required member injection option is ",(0,s.jsx)(i.strong,{children:"enabled"})," by default."]})}),"\n",(0,s.jsx)(i.h2,{id:"injection-parameters",children:"Injection parameters"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withinjectionparameters",children:(0,s.jsx)(i.code,{children:"WithInjectionParameters"})}),(0,s.jsx)(i.p,{children:"Sets multiple injection parameters for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithInjectionParameters(new KeyValuePair("logger", new ConsoleLogger()));\n'})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"withinjectionparameter",children:(0,s.jsx)(i.code,{children:"WithInjectionParameter"})}),(0,s.jsx)(i.p,{children:"Sets a single injection parameter for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .WithInjectionParameter("logger", new ConsoleLogger());\n'})})})]}),"\n",(0,s.jsx)(i.h2,{id:"factory",children:"Factory"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about the concept of factory registration ",(0,s.jsx)(i.a,{href:"/docs/guides/advanced-registration?id=factory-registration",children:"here"}),"."]}),"\n",(0,s.jsxs)(o.A,{children:[(0,s.jsxs)(c.A,{value:"Parameterized",label:"Parameterized",children:[(0,s.jsxs)(i.p,{children:[(0,s.jsx)(i.strong,{children:"WithFactory"})," - Sets a factory delegate that could take various number of pre-resolved dependencies as parameters and returns the service instance."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"// 1 parameter factory\ncontainer.Register(options => options\n .WithFactory(logger => new UserRepository(logger));\n\n// 2 parameters factory\ncontainer.Register(options => options\n .WithFactory((logger, context) => new UserRepository(logger, context));\n\n// 3 parameters factory\ncontainer.Register(options => options\n .WithFactory((logger, context, options) => \n new UserRepository(logger, context, options));\n\n// 4 parameters factory\ncontainer.Register(options => options\n .WithFactory((logger, connection, options, validator) => \n new UserRepository(logger, connection, options, validator));\n\n// 5 parameters factory\ncontainer.Register(options => options\n .WithFactory(\n (logger, connection, options, validator, permissionManager) => \n new UserRepository(logger, connection, options, validator, permissionManager));\n"})}),(0,s.jsxs)(i.p,{children:["You can also get the current ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#dependency-resolver",children:"dependency resolver"})," as a pre-resolved parameter:"]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithFactory((logger, resolver) => \n new UserRepository(logger, resolver.Resolve())));\n"})})]}),(0,s.jsxs)(c.A,{value:"Parameter-less",label:"Parameter-less",children:[(0,s.jsxs)(i.p,{children:[(0,s.jsx)(i.strong,{children:"WithFactory"})," - Sets a parameter-less factory delegate that returns the service instance."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithFactory(()) => new UserRepository(new ConsoleLogger()));\n"})})]}),(0,s.jsxs)(c.A,{value:"Resolver parameter",label:"Resolver parameter",children:[(0,s.jsxs)(i.p,{children:[(0,s.jsx)(i.strong,{children:"WithFactory"})," - Sets a factory delegate that takes an ",(0,s.jsx)(i.code,{children:"IDependencyResolver"})," as parameter and returns the service instance."]}),(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithFactory(resolver => new UserRepository(resolver.Resolve()));\n"})})]})]}),"\n",(0,s.jsx)(i.admonition,{type:"info",children:(0,s.jsxs)(i.p,{children:["All factory configuration method has an ",(0,s.jsx)(i.code,{children:"isCompiledLambda"})," parameter which should be set to ",(0,s.jsx)(i.code,{children:"true"})," if the passed delegate is compiled from an ",(0,s.jsx)(i.code,{children:"Expression"})," tree."]})}),"\n",(0,s.jsx)(i.h2,{id:"scope-definition",children:"Scope definition"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about the concept of defined scopes ",(0,s.jsx)(i.a,{href:"/docs/guides/scopes?id=service-as-scope",children:"here"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"innamedscope",children:(0,s.jsx)(i.code,{children:"InNamedScope"})}),(0,s.jsx)(i.p,{children:"Sets a scope name condition for the registration; it will be used only when a scope with the same name requests it."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .InNamedScope("UserRepo"));\n'})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"inscopedefinedby",children:(0,s.jsx)(i.code,{children:"InScopeDefinedBy"})}),(0,s.jsx)(i.p,{children:"Sets a condition for the registration; it will be used only within the scope defined by the given type."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .InScopeDefinedBy());\ncontainer.Register(options => options\n .DefinesScope());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"definesscope",children:(0,s.jsx)(i.code,{children:"DefinesScope"})}),(0,s.jsxs)(i.p,{children:["This registration is used as a logical scope for it's dependencies. Dependencies registered with ",(0,s.jsx)(i.code,{children:"InNamedScope()"})," with the same name are preferred during resolution.\nWhen the ",(0,s.jsx)(i.code,{children:"name"})," is not set, the ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," is used as the name. Dependencies registered with ",(0,s.jsx)(i.code,{children:"InScopeDefinedBy()"})," are selected."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .DefinesScope("UserRepo"));\n\n// or\ncontainer.Register(options => options\n .DefinesScope());\n'})})})]}),"\n",(0,s.jsx)(i.h2,{id:"decorator-specific",children:"Decorator specific"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about decorators ",(0,s.jsx)(i.a,{href:"/docs/advanced/decorators",children:"here"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"whendecoratedserviceis",children:(0,s.jsx)(i.code,{children:"WhenDecoratedServiceIs"})}),(0,s.jsx)(i.p,{children:"Sets a decorated target condition for the registration."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.RegisterDecorator(options => options\n .WhenDecoratedServiceIs());\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"whendecoratedservicehas",children:(0,s.jsx)(i.code,{children:"WhenDecoratedServiceHas"})}),(0,s.jsx)(i.p,{children:"Sets an attribute condition that the decorated target has to satisfy."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"container.RegisterDecorator(options => options\n .WhenDecoratedServiceHas());\n"})})})]}),"\n",(0,s.jsx)(i.h2,{id:"unknown-registration-specific",children:"Unknown registration specific"}),"\n",(0,s.jsxs)(i.p,{children:["You can read more about unknown type resolution ",(0,s.jsx)(i.a,{href:"/docs/advanced/special-resolution-cases#unknown-type-resolution",children:"here"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"setimplementationtype",children:(0,s.jsx)(i.code,{children:"SetImplementationType"})}),(0,s.jsxs)(i.p,{children:["Sets the current registration's ",(0,s.jsx)(i.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"}),"."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"var container = new StashboxContainer(c => c.WithUnknownTypeResolution(config =>\n{\n if (config.ServiceType == typeof(IService))\n config.SetImplementationType(typeof(Service));\n}));\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(i.h3,{id:"skip",children:(0,s.jsx)(i.code,{children:"Skip"})}),(0,s.jsx)(i.p,{children:"Marks the current unknown type registration as skipped."})]}),(0,s.jsx)("div",{children:(0,s.jsx)(i.pre,{children:(0,s.jsx)(i.code,{className:"language-cs",children:"var container = new StashboxContainer(c => c.WithUnknownTypeResolution(config =>\n{\n if (config.ServiceType == typeof(IService))\n config.Skip();\n}));\n"})})})]})]})}function g(e={}){const{wrapper:i}={...(0,t.R)(),...e.components};return i?(0,s.jsx)(i,{...e,children:(0,s.jsx)(u,{...e})}):u(e)}},9365:(e,i,n)=>{n.d(i,{A:()=>o});n(6540);var s=n(870);const t={tabItem:"tabItem_Ymn6"};var r=n(4848);function o(e){let{children:i,hidden:n,className:o}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,s.A)(t.tabItem,o),hidden:n,children:i})}},1470:(e,i,n)=>{n.d(i,{A:()=>I});var s=n(6540),t=n(870),r=n(3104),o=n(6347),c=n(205),l=n(7485),a=n(1682),d=n(9466);function h(e){return s.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,s.isValidElement)(e)&&function(e){const{props:i}=e;return!!i&&"object"==typeof i&&"value"in i}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function p(e){const{values:i,children:n}=e;return(0,s.useMemo)((()=>{const e=i??function(e){return h(e).map((e=>{let{props:{value:i,label:n,attributes:s,default:t}}=e;return{value:i,label:n,attributes:s,default:t}}))}(n);return function(e){const i=(0,a.X)(e,((e,i)=>e.value===i.value));if(i.length>0)throw new Error(`Docusaurus error: Duplicate values "${i.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[i,n])}function u(e){let{value:i,tabValues:n}=e;return n.some((e=>e.value===i))}function g(e){let{queryString:i=!1,groupId:n}=e;const t=(0,o.W6)(),r=function(e){let{queryString:i=!1,groupId:n}=e;if("string"==typeof i)return i;if(!1===i)return null;if(!0===i&&!n)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:i,groupId:n});return[(0,l.aZ)(r),(0,s.useCallback)((e=>{if(!r)return;const i=new URLSearchParams(t.location.search);i.set(r,e),t.replace({...t.location,search:i.toString()})}),[r,t])]}function j(e){const{defaultValue:i,queryString:n=!1,groupId:t}=e,r=p(e),[o,l]=(0,s.useState)((()=>function(e){let{defaultValue:i,tabValues:n}=e;if(0===n.length)throw new Error("Docusaurus error: the component requires at least one children component");if(i){if(!u({value:i,tabValues:n}))throw new Error(`Docusaurus error: The has a defaultValue "${i}" but none of its children has the corresponding value. Available values are: ${n.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return i}const s=n.find((e=>e.default))??n[0];if(!s)throw new Error("Unexpected error: 0 tabValues");return s.value}({defaultValue:i,tabValues:r}))),[a,h]=g({queryString:n,groupId:t}),[j,x]=function(e){let{groupId:i}=e;const n=function(e){return e?`docusaurus.tab.${e}`:null}(i),[t,r]=(0,d.Dv)(n);return[t,(0,s.useCallback)((e=>{n&&r.set(e)}),[n,r])]}({groupId:t}),m=(()=>{const e=a??j;return u({value:e,tabValues:r})?e:null})();(0,c.A)((()=>{m&&l(m)}),[m]);return{selectedValue:o,selectValue:(0,s.useCallback)((e=>{if(!u({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);l(e),h(e),x(e)}),[h,x,r]),tabValues:r}}var x=n(2303);const m={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var v=n(4848);function f(e){let{className:i,block:n,selectedValue:s,selectValue:o,tabValues:c}=e;const l=[],{blockElementScrollPositionUntilNextRender:a}=(0,r.a_)(),d=e=>{const i=e.currentTarget,n=l.indexOf(i),t=c[n].value;t!==s&&(a(i),o(t))},h=e=>{let i=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const n=l.indexOf(e.currentTarget)+1;i=l[n]??l[0];break}case"ArrowLeft":{const n=l.indexOf(e.currentTarget)-1;i=l[n]??l[l.length-1];break}}i?.focus()};return(0,v.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,t.A)("tabs",{"tabs--block":n},i),children:c.map((e=>{let{value:i,label:n,attributes:r}=e;return(0,v.jsx)("li",{role:"tab",tabIndex:s===i?0:-1,"aria-selected":s===i,ref:e=>l.push(e),onKeyDown:h,onClick:d,...r,className:(0,t.A)("tabs__item",m.tabItem,r?.className,{"tabs__item--active":s===i}),children:n??i},i)}))})}function y(e){let{lazy:i,children:n,selectedValue:t}=e;const r=(Array.isArray(n)?n:[n]).filter(Boolean);if(i){const e=r.find((e=>e.props.value===t));return e?(0,s.cloneElement)(e,{className:"margin-top--md"}):null}return(0,v.jsx)("div",{className:"margin-top--md",children:r.map(((e,i)=>(0,s.cloneElement)(e,{key:i,hidden:e.props.value!==t})))})}function b(e){const i=j(e);return(0,v.jsxs)("div",{className:(0,t.A)("tabs-container",m.tabList),children:[(0,v.jsx)(f,{...e,...i}),(0,v.jsx)(y,{...e,...i})]})}function I(e){const i=(0,x.A)();return(0,v.jsx)(b,{...e,children:h(e.children)},String(i))}},7470:(e,i,n)=>{n.d(i,{A:()=>o});var s=n(6540);const t={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var r=n(4848);function o(e){let{children:i}=e,n=s.Children.toArray(i).filter((e=>e));return(0,r.jsxs)("div",{className:t.codeDescContainer,children:[(0,r.jsx)("div",{className:t.desc,children:n[0]}),(0,r.jsx)("div",{className:t.example,children:n[1]})]})}},8453:(e,i,n)=>{n.d(i,{R:()=>o,x:()=>c});var s=n(6540);const t={},r=s.createContext(t);function o(e){const i=s.useContext(r);return s.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function c(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:o(e.components),s.createElement(r.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/5b5f0b93.7ea32b58.js b/assets/js/5b5f0b93.23961e77.js similarity index 99% rename from assets/js/5b5f0b93.7ea32b58.js rename to assets/js/5b5f0b93.23961e77.js index 01880485..f921bbbf 100644 --- a/assets/js/5b5f0b93.7ea32b58.js +++ b/assets/js/5b5f0b93.23961e77.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[545],{3584:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>l,contentTitle:()=>c,default:()=>p,frontMatter:()=>a,metadata:()=>o,toc:()=>d});var t=i(4848),r=i(8453),s=i(7470);const a={},c="Child containers",o={id:"advanced/child-containers",title:"Child containers",description:"With child containers, you can build up parent-child relationships between containers. This means you can have a different subset of services present in a child than in the parent container.",source:"@site/docs/advanced/child-containers.md",sourceDirName:"advanced",slug:"/advanced/child-containers",permalink:"/stashbox/docs/advanced/child-containers",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/advanced/child-containers.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Wrappers & resolvers",permalink:"/stashbox/docs/advanced/wrappers-resolvers"},next:{title:"Special resolution cases",permalink:"/stashbox/docs/advanced/special-resolution-cases"}},l={},d=[{value:"Example",id:"example",level:2},{value:"Accessing child containers",id:"accessing-child-containers",level:2},{value:"Resolution behavior",id:"resolution-behavior",level:2},{value:"Re-building singletons",id:"re-building-singletons",level:2},{value:"Nested child containers",id:"nested-child-containers",level:2},{value:"Dispose",id:"dispose",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,r.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{id:"child-containers",children:"Child containers"}),"\n",(0,t.jsx)(n.p,{children:"With child containers, you can build up parent-child relationships between containers. This means you can have a different subset of services present in a child than in the parent container."}),"\n",(0,t.jsxs)(n.p,{children:["When a dependency is missing from the child container during a resolution request, the parent will be asked to resolve the missing service. If it's found there, the parent will return only the service's registration, and the resolution request will jump back to the child. Also, child registrations with the same ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," will override the parent's services."]}),"\n",(0,t.jsxs)(n.p,{children:["Resolving ",(0,t.jsx)(n.code,{children:"IEnumerable"})," and ",(0,t.jsx)(n.a,{href:"/docs/advanced/decorators",children:"decorators"})," also considers parent containers by default. However, this behavior can be controlled with the ",(0,t.jsx)(n.a,{href:"#resolution-behavior",children:(0,t.jsx)(n.code,{children:"ResolutionBehavior"})})," parameter."]}),"\n",(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["Child containers are the foundation of the ",(0,t.jsx)(n.a,{href:"https://github.com/z4kn4fein/stashbox-extensions-dependencyinjection#multitenant",children:"ASP.NET Core multi-tenant extension"}),"."]})}),"\n",(0,t.jsx)(n.h2,{id:"example",children:"Example"}),"\n",(0,t.jsx)(n.p,{children:"Here is an example case:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"interface IDependency {}\n\nclass B : IDependency {}\nclass C : IDependency {}\n\nclass A \n{\n public A(IDependency dependency)\n { }\n}\n\nusing (var container = new StashboxContainer())\n{\n // register 'A' into the parent container.\n container.Register();\n\n // register 'B' as a dependency into the parent container.\n container.Register();\n\n var child = container.CreateChildContainer()\n \n // register 'C' as a dependency into the child container.\n child.Register();\n \n // 'A' is resolved from the parent and gets\n // 'C' as IDependency because the resolution\n // request was initiated on the child.\n A fromChild = child.Resolve();\n\n // 'A' gets 'B' as IDependency because the \n // resolution request was initiated on the parent.\n A fromParent = container.Resolve();\n} // using will dispose the parent along with the child.\n"})}),"\n",(0,t.jsxs)(n.p,{children:["Let's see what's happening when we request ",(0,t.jsx)(n.code,{children:"A"})," from the ",(0,t.jsx)(n.em,{children:"child"}),":"]}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"A"})," not found in the ",(0,t.jsx)(n.em,{children:"child"}),", go up to the ",(0,t.jsx)(n.em,{children:"parent"})," and check there."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"A"})," found in the ",(0,t.jsx)(n.em,{children:"parent"}),", resolve."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"A"})," depends on ",(0,t.jsx)(n.code,{children:"IDependency"}),", go back to the ",(0,t.jsx)(n.em,{children:"child"})," and search ",(0,t.jsx)(n.code,{children:"IDependency"})," implementations."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"C"})," found in the ",(0,t.jsx)(n.em,{children:"child"}),", it does not have any dependencies, instantiate."]}),"\n",(0,t.jsxs)(n.li,{children:["Inject the new ",(0,t.jsx)(n.code,{children:"C"})," instance into ",(0,t.jsx)(n.code,{children:"A"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:["All dependencies are resolved; return ",(0,t.jsx)(n.code,{children:"A"}),"."]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["When we make the same request on the parent, everything will go as usual because we have all dependencies in place. ",(0,t.jsx)(n.code,{children:"B"})," will be injected into ",(0,t.jsx)(n.code,{children:"A"}),"."]}),"\n",(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["You can ",(0,t.jsx)(n.a,{href:"/docs/configuration/container-configuration",children:"re-configure"})," child containers with the ",(0,t.jsx)(n.code,{children:".Configure()"})," method. It doesn't affect the parent container's configuration."]})}),"\n",(0,t.jsx)(n.h2,{id:"accessing-child-containers",children:"Accessing child containers"}),"\n",(0,t.jsxs)(n.p,{children:["You can identify child containers with the ",(0,t.jsx)(n.code,{children:"identifier"})," parameter of ",(0,t.jsx)(n.code,{children:"CreateChildContainer()"}),". Later, you can retrieve the given child container by passing its ID to ",(0,t.jsx)(n.code,{children:"GetChildContainer()"}),"."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'using var container = new StashboxContainer();\ncontainer.CreateChildContainer("child");\n// ...\n\nvar child = container.GetChildContainer("child");\n'})}),"\n",(0,t.jsxs)(n.p,{children:["Also, each child container created by a container is available through the ",(0,t.jsx)(n.code,{children:"IStashboxContainer.ChildContainers"})," propert."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'using var container = new StashboxContainer();\ncontainer.CreateChildContainer("child1");\ncontainer.CreateChildContainer("child2");\n// ...\n\nforeach (var child in container.ChildContainers)\n{\n var id = child.Key;\n var childContainer = child.Value;\n}\n'})}),"\n",(0,t.jsx)(n.h2,{id:"resolution-behavior",children:"Resolution behavior"}),"\n",(0,t.jsxs)(n.p,{children:["You can control which level of the container hierarchy can participate in the service resolution with the ",(0,t.jsx)(n.code,{children:"ResolutionBehavior"})," parameter."]}),"\n",(0,t.jsx)(n.p,{children:"Possible values:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"Default"}),": The default behavior, it's used when the parameter is not specified. Its value is ",(0,t.jsx)(n.code,{children:"Parent | Current"}),", so the parents and the current container (which initiated the resolution request) can participate in the resolution request's service selection."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"Parent"}),": Indicates that parent containers (including indirect all ancestors) can participate in the resolution request's service selection."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"Current"}),": Indicates that the current container (which initiated the resolution request) can participate in the service selection."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"ParentDependency"}),": Indicates that parent containers (including indirect all ancestors) can only provide dependencies for services that are already selected for resolution."]}),"\n"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-csharp",children:"interface IService {}\n\nclass A : IService {}\nclass B : IService {}\n\nusing (var container = new StashboxContainer())\n{\n // register 'A' into the parent container.\n container.Register();\n\n var child = container.CreateChildContainer()\n \n // register 'B' into the child container.\n child.Register();\n \n // 'A' is resolved because only parent\n // can participate in the resolution request.\n IService withParent = child.Resolve(ResolutionBehavior.Parent);\n\n // Only 'B' is in the collection because\n // only the caller container can take part\n // in the resolution request.\n IEnumerable allWithCurrent = child.Resolve>(ResolutionBehavior.Current);\n \n // Both 'A' and 'B' is in the collection\n // because both the parent and the caller container\n // participates in the resolution request.\n IEnumerable all = child.Resolve>(ResolutionBehavior.Current | ResolutionBehavior.Parent);\n} // using will dispose the parent along with the child.\n"})}),"\n",(0,t.jsx)(n.h2,{id:"re-building-singletons",children:"Re-building singletons"}),"\n",(0,t.jsxs)(n.p,{children:["By default, singletons are instantiated and stored only in those containers that registered them. However, you can enable the re-instantiation of singletons in child containers with the ",(0,t.jsx)(n.code,{children:".WithReBuildSingletonsInChildContainer()"})," ",(0,t.jsx)(n.a,{href:"/docs/configuration/container-configuration#re-build-singletons-in-child-containers",children:"container configuration option"}),"."]}),"\n",(0,t.jsx)(n.p,{children:"If it's enabled, all singletons will be re-created in those containers that initiated the resolution request. By this, re-built singletons can use overridden dependencies from child containers."}),"\n",(0,t.jsx)(n.p,{children:"Re-building in child containers does not affect the singletons instantiated in the parent container."}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"interface IDependency {}\n\nclass B : IDependency {}\nclass C : IDependency {}\n\nclass A \n{\n public A(IDependency dependency)\n { }\n}\n\nusing (var container = new StashboxContainer(options => options.WithReBuildSingletonsInChildContainer()))\n{\n // register 'A' as a singleton into the parent container.\n container.RegisterSingleton();\n\n // register 'B' as a dependency into the parent container.\n container.Register();\n\n // 'A' gets 'B' as IDependency and will be stored\n // in the parent container as a singleton.\n A fromParent = container.Resolve();\n\n var child = container.CreateChildContainer();\n \n // register 'C' as a dependency into the child container.\n child.Register();\n\n // a new 'A' singleton will be created in\n // the child container with 'C' as IDependency.\n A fromChild = child.Resolve();\n} // using will dispose the parent along with the child.\n"})}),"\n",(0,t.jsx)(n.h2,{id:"nested-child-containers",children:"Nested child containers"}),"\n",(0,t.jsxs)(s.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["You can build up a hierarchical tree structure from containers by creating more child containers with the ",(0,t.jsx)(n.code,{children:".CreateChildContainer()"})," method."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"using var container = new StashboxContainer();\n\nvar child1 = container.CreateChildContainer();\nvar child2 = child1.CreateChildContainer();\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"dispose",children:"Dispose"}),"\n",(0,t.jsxs)(n.p,{children:["By default, the parent container's disposal also disposes its child containers. You can control this behavior with the ",(0,t.jsx)(n.code,{children:"CreateChildContainer()"})," method's ",(0,t.jsx)(n.code,{children:"attachToParent"})," boolean parameter."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"using (var container = new StashboxContainer())\n{\n using (var child1 = container.CreateChildContainer(attachToParent: false))\n {\n } // child1 will be disposed only once here.\n \n var child2 = container.CreateChildContainer();\n var child3 = container.CreateChildContainer();\n} // using will dispose the parent along with child2 and child3.\n"})}),"\n",(0,t.jsx)(n.p,{children:"You can safely dispose a child even if it's attached to its parent, in this case the parent's disposal will not dispose the already disposed child."}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"using (var container = new StashboxContainer())\n{\n using (var child1 = container.CreateChildContainer())\n {\n } // child1 will be disposed only once here.\n \n var child2 = container.CreateChildContainer();\n} // using will dispose only the parent and child2.\n"})})]})}function p(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}},7470:(e,n,i)=>{i.d(n,{A:()=>a});var t=i(6540);const r={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var s=i(4848);function a(e){let{children:n}=e,i=t.Children.toArray(n).filter((e=>e));return(0,s.jsxs)("div",{className:r.codeDescContainer,children:[(0,s.jsx)("div",{className:r.desc,children:i[0]}),(0,s.jsx)("div",{className:r.example,children:i[1]})]})}},8453:(e,n,i)=>{i.d(n,{R:()=>a,x:()=>c});var t=i(6540);const r={},s=t.createContext(r);function a(e){const n=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:a(e.components),t.createElement(s.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[545],{3584:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>l,contentTitle:()=>c,default:()=>p,frontMatter:()=>a,metadata:()=>o,toc:()=>d});var t=i(4848),r=i(8453),s=i(7470);const a={},c="Child containers",o={id:"advanced/child-containers",title:"Child containers",description:"With child containers, you can build up parent-child relationships between containers. This means you can have a different subset of services present in a child than in the parent container.",source:"@site/docs/advanced/child-containers.md",sourceDirName:"advanced",slug:"/advanced/child-containers",permalink:"/stashbox/docs/advanced/child-containers",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/advanced/child-containers.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Wrappers & resolvers",permalink:"/stashbox/docs/advanced/wrappers-resolvers"},next:{title:"Special resolution cases",permalink:"/stashbox/docs/advanced/special-resolution-cases"}},l={},d=[{value:"Example",id:"example",level:2},{value:"Accessing child containers",id:"accessing-child-containers",level:2},{value:"Resolution behavior",id:"resolution-behavior",level:2},{value:"Re-building singletons",id:"re-building-singletons",level:2},{value:"Nested child containers",id:"nested-child-containers",level:2},{value:"Dispose",id:"dispose",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,r.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{id:"child-containers",children:"Child containers"}),"\n",(0,t.jsx)(n.p,{children:"With child containers, you can build up parent-child relationships between containers. This means you can have a different subset of services present in a child than in the parent container."}),"\n",(0,t.jsxs)(n.p,{children:["When a dependency is missing from the child container during a resolution request, the parent will be asked to resolve the missing service. If it's found there, the parent will return only the service's registration, and the resolution request will jump back to the child. Also, child registrations with the same ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," will override the parent's services."]}),"\n",(0,t.jsxs)(n.p,{children:["Resolving ",(0,t.jsx)(n.code,{children:"IEnumerable"})," and ",(0,t.jsx)(n.a,{href:"/docs/advanced/decorators",children:"decorators"})," also considers parent containers by default. However, this behavior can be controlled with the ",(0,t.jsx)(n.a,{href:"#resolution-behavior",children:(0,t.jsx)(n.code,{children:"ResolutionBehavior"})})," parameter."]}),"\n",(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["Child containers are the foundation of the ",(0,t.jsx)(n.a,{href:"https://github.com/z4kn4fein/stashbox-extensions-dependencyinjection#multitenant",children:"ASP.NET Core multi-tenant extension"}),"."]})}),"\n",(0,t.jsx)(n.h2,{id:"example",children:"Example"}),"\n",(0,t.jsx)(n.p,{children:"Here is an example case:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"interface IDependency {}\n\nclass B : IDependency {}\nclass C : IDependency {}\n\nclass A \n{\n public A(IDependency dependency)\n { }\n}\n\nusing (var container = new StashboxContainer())\n{\n // register 'A' into the parent container.\n container.Register();\n\n // register 'B' as a dependency into the parent container.\n container.Register();\n\n var child = container.CreateChildContainer()\n \n // register 'C' as a dependency into the child container.\n child.Register();\n \n // 'A' is resolved from the parent and gets\n // 'C' as IDependency because the resolution\n // request was initiated on the child.\n A fromChild = child.Resolve();\n\n // 'A' gets 'B' as IDependency because the \n // resolution request was initiated on the parent.\n A fromParent = container.Resolve();\n} // using will dispose the parent along with the child.\n"})}),"\n",(0,t.jsxs)(n.p,{children:["Let's see what's happening when we request ",(0,t.jsx)(n.code,{children:"A"})," from the ",(0,t.jsx)(n.em,{children:"child"}),":"]}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"A"})," not found in the ",(0,t.jsx)(n.em,{children:"child"}),", go up to the ",(0,t.jsx)(n.em,{children:"parent"})," and check there."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"A"})," found in the ",(0,t.jsx)(n.em,{children:"parent"}),", resolve."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"A"})," depends on ",(0,t.jsx)(n.code,{children:"IDependency"}),", go back to the ",(0,t.jsx)(n.em,{children:"child"})," and search ",(0,t.jsx)(n.code,{children:"IDependency"})," implementations."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"C"})," found in the ",(0,t.jsx)(n.em,{children:"child"}),", it does not have any dependencies, instantiate."]}),"\n",(0,t.jsxs)(n.li,{children:["Inject the new ",(0,t.jsx)(n.code,{children:"C"})," instance into ",(0,t.jsx)(n.code,{children:"A"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:["All dependencies are resolved; return ",(0,t.jsx)(n.code,{children:"A"}),"."]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["When we make the same request on the parent, everything will go as usual because we have all dependencies in place. ",(0,t.jsx)(n.code,{children:"B"})," will be injected into ",(0,t.jsx)(n.code,{children:"A"}),"."]}),"\n",(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["You can ",(0,t.jsx)(n.a,{href:"/docs/configuration/container-configuration",children:"re-configure"})," child containers with the ",(0,t.jsx)(n.code,{children:".Configure()"})," method. It doesn't affect the parent container's configuration."]})}),"\n",(0,t.jsx)(n.h2,{id:"accessing-child-containers",children:"Accessing child containers"}),"\n",(0,t.jsxs)(n.p,{children:["You can identify child containers with the ",(0,t.jsx)(n.code,{children:"identifier"})," parameter of ",(0,t.jsx)(n.code,{children:"CreateChildContainer()"}),". Later, you can retrieve the given child container by passing its ID to ",(0,t.jsx)(n.code,{children:"GetChildContainer()"}),"."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'using var container = new StashboxContainer();\ncontainer.CreateChildContainer("child");\n// ...\n\nvar child = container.GetChildContainer("child");\n'})}),"\n",(0,t.jsxs)(n.p,{children:["Also, each child container created by a container is available through the ",(0,t.jsx)(n.code,{children:"IStashboxContainer.ChildContainers"})," propert."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'using var container = new StashboxContainer();\ncontainer.CreateChildContainer("child1");\ncontainer.CreateChildContainer("child2");\n// ...\n\nforeach (var child in container.ChildContainers)\n{\n var id = child.Key;\n var childContainer = child.Value;\n}\n'})}),"\n",(0,t.jsx)(n.h2,{id:"resolution-behavior",children:"Resolution behavior"}),"\n",(0,t.jsxs)(n.p,{children:["You can control which level of the container hierarchy can participate in the service resolution with the ",(0,t.jsx)(n.code,{children:"ResolutionBehavior"})," parameter."]}),"\n",(0,t.jsx)(n.p,{children:"Possible values:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"Default"}),": The default behavior, it's used when the parameter is not specified. Its value is ",(0,t.jsx)(n.code,{children:"Parent | Current"}),", so the parents and the current container (which initiated the resolution request) can participate in the resolution request's service selection."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"Parent"}),": Indicates that parent containers (including indirect all ancestors) can participate in the resolution request's service selection."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"Current"}),": Indicates that the current container (which initiated the resolution request) can participate in the service selection."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"ParentDependency"}),": Indicates that parent containers (including indirect all ancestors) can only provide dependencies for services that are already selected for resolution."]}),"\n"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-csharp",children:"interface IService {}\n\nclass A : IService {}\nclass B : IService {}\n\nusing (var container = new StashboxContainer())\n{\n // register 'A' into the parent container.\n container.Register();\n\n var child = container.CreateChildContainer()\n \n // register 'B' into the child container.\n child.Register();\n \n // 'A' is resolved because only parent\n // can participate in the resolution request.\n IService withParent = child.Resolve(ResolutionBehavior.Parent);\n\n // Only 'B' is in the collection because\n // only the caller container can take part\n // in the resolution request.\n IEnumerable allWithCurrent = child.Resolve>(ResolutionBehavior.Current);\n \n // Both 'A' and 'B' is in the collection\n // because both the parent and the caller container\n // participates in the resolution request.\n IEnumerable all = child.Resolve>(ResolutionBehavior.Current | ResolutionBehavior.Parent);\n} // using will dispose the parent along with the child.\n"})}),"\n",(0,t.jsx)(n.h2,{id:"re-building-singletons",children:"Re-building singletons"}),"\n",(0,t.jsxs)(n.p,{children:["By default, singletons are instantiated and stored only in those containers that registered them. However, you can enable the re-instantiation of singletons in child containers with the ",(0,t.jsx)(n.code,{children:".WithReBuildSingletonsInChildContainer()"})," ",(0,t.jsx)(n.a,{href:"/docs/configuration/container-configuration#re-build-singletons-in-child-containers",children:"container configuration option"}),"."]}),"\n",(0,t.jsx)(n.p,{children:"If it's enabled, all singletons will be re-created in those containers that initiated the resolution request. By this, re-built singletons can use overridden dependencies from child containers."}),"\n",(0,t.jsx)(n.p,{children:"Re-building in child containers does not affect the singletons instantiated in the parent container."}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"interface IDependency {}\n\nclass B : IDependency {}\nclass C : IDependency {}\n\nclass A \n{\n public A(IDependency dependency)\n { }\n}\n\nusing (var container = new StashboxContainer(options => options.WithReBuildSingletonsInChildContainer()))\n{\n // register 'A' as a singleton into the parent container.\n container.RegisterSingleton();\n\n // register 'B' as a dependency into the parent container.\n container.Register();\n\n // 'A' gets 'B' as IDependency and will be stored\n // in the parent container as a singleton.\n A fromParent = container.Resolve();\n\n var child = container.CreateChildContainer();\n \n // register 'C' as a dependency into the child container.\n child.Register();\n\n // a new 'A' singleton will be created in\n // the child container with 'C' as IDependency.\n A fromChild = child.Resolve();\n} // using will dispose the parent along with the child.\n"})}),"\n",(0,t.jsx)(n.h2,{id:"nested-child-containers",children:"Nested child containers"}),"\n",(0,t.jsxs)(s.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["You can build up a hierarchical tree structure from containers by creating more child containers with the ",(0,t.jsx)(n.code,{children:".CreateChildContainer()"})," method."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"using var container = new StashboxContainer();\n\nvar child1 = container.CreateChildContainer();\nvar child2 = child1.CreateChildContainer();\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"dispose",children:"Dispose"}),"\n",(0,t.jsxs)(n.p,{children:["By default, the parent container's disposal also disposes its child containers. You can control this behavior with the ",(0,t.jsx)(n.code,{children:"CreateChildContainer()"})," method's ",(0,t.jsx)(n.code,{children:"attachToParent"})," boolean parameter."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"using (var container = new StashboxContainer())\n{\n using (var child1 = container.CreateChildContainer(attachToParent: false))\n {\n } // child1 will be disposed only once here.\n \n var child2 = container.CreateChildContainer();\n var child3 = container.CreateChildContainer();\n} // using will dispose the parent along with child2 and child3.\n"})}),"\n",(0,t.jsx)(n.p,{children:"You can safely dispose a child even if it's attached to its parent, in this case the parent's disposal will not dispose the already disposed child."}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"using (var container = new StashboxContainer())\n{\n using (var child1 = container.CreateChildContainer())\n {\n } // child1 will be disposed only once here.\n \n var child2 = container.CreateChildContainer();\n} // using will dispose only the parent and child2.\n"})})]})}function p(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}},7470:(e,n,i)=>{i.d(n,{A:()=>a});var t=i(6540);const r={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var s=i(4848);function a(e){let{children:n}=e,i=t.Children.toArray(n).filter((e=>e));return(0,s.jsxs)("div",{className:r.codeDescContainer,children:[(0,s.jsx)("div",{className:r.desc,children:i[0]}),(0,s.jsx)("div",{className:r.example,children:i[1]})]})}},8453:(e,n,i)=>{i.d(n,{R:()=>a,x:()=>c});var t=i(6540);const r={},s=t.createContext(r);function a(e){const n=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:a(e.components),t.createElement(s.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/61ac6d0c.f2525d28.js b/assets/js/61ac6d0c.aa2e8309.js similarity index 99% rename from assets/js/61ac6d0c.f2525d28.js rename to assets/js/61ac6d0c.aa2e8309.js index 30d10aac..f26e167e 100644 --- a/assets/js/61ac6d0c.f2525d28.js +++ b/assets/js/61ac6d0c.aa2e8309.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[429],{9267:(e,n,r)=>{r.r(n),r.d(n,{assets:()=>u,contentTitle:()=>c,default:()=>v,frontMatter:()=>l,metadata:()=>d,toc:()=>p});var s=r(4848),t=r(8453),i=r(7470),a=r(1470),o=r(9365);const l={},c="Wrappers & resolvers",d={id:"advanced/wrappers-resolvers",title:"Wrappers & resolvers",description:"Stashbox uses so-called Wrapper and Resolver implementations to handle special resolution requests that none of the service registrations can fulfill. Functionalities like wrapper and unknown type resolution, cross-container requests, optional and default value injection are all built with resolvers.",source:"@site/docs/advanced/wrappers-resolvers.md",sourceDirName:"advanced",slug:"/advanced/wrappers-resolvers",permalink:"/stashbox/docs/advanced/wrappers-resolvers",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/advanced/wrappers-resolvers.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Decorators",permalink:"/stashbox/docs/advanced/decorators"},next:{title:"Child containers",permalink:"/stashbox/docs/advanced/child-containers"}},u={},p=[{value:"Pre-defined wrappers & resolvers",id:"pre-defined-wrappers--resolvers",level:2},{value:"Wrappers",id:"wrappers",level:2},{value:"Enumerable",id:"enumerable",level:3},{value:"Lazy",id:"lazy",level:3},{value:"Delegate",id:"delegate",level:3},{value:"Metadata & Tuple",id:"metadata--tuple",level:3},{value:"KeyValuePair & ReadOnlyKeyValue",id:"keyvaluepair--readonlykeyvalue",level:3},{value:"User-defined wrappers & resolvers",id:"user-defined-wrappers--resolvers",level:2},{value:"Visiting order",id:"visiting-order",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.h1,{id:"wrappers--resolvers",children:"Wrappers & resolvers"}),"\n",(0,s.jsxs)(n.p,{children:["Stashbox uses so-called ",(0,s.jsx)(n.em,{children:"Wrapper"})," and ",(0,s.jsx)(n.em,{children:"Resolver"})," implementations to handle special resolution requests that none of the ",(0,s.jsx)(n.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"service registrations"})," can fulfill. Functionalities like ",(0,s.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#wrappers",children:"wrapper"})," and ",(0,s.jsx)(n.a,{href:"/docs/advanced/special-resolution-cases#unknown-type-resolution",children:"unknown type"})," resolution, ",(0,s.jsx)(n.a,{href:"/docs/advanced/child-containers",children:"cross-container requests"}),", ",(0,s.jsx)(n.a,{href:"/docs/advanced/special-resolution-cases#optional-value-injection",children:"optional"})," and ",(0,s.jsx)(n.a,{href:"/docs/advanced/special-resolution-cases#default-value-injection",children:"default value"})," injection are all built with resolvers."]}),"\n",(0,s.jsx)(n.h2,{id:"pre-defined-wrappers--resolvers",children:"Pre-defined wrappers & resolvers"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"EnumerableWrapper"}),": Used to resolve a collection of services wrapped in one of the collection interfaces that a .NET ",(0,s.jsx)(n.code,{children:"Array"})," implements. (",(0,s.jsx)(n.code,{children:"IEnumerable<>"}),", ",(0,s.jsx)(n.code,{children:"IList<>"}),", ",(0,s.jsx)(n.code,{children:"ICollection<>"}),", ",(0,s.jsx)(n.code,{children:"IReadOnlyList<>"}),", ",(0,s.jsx)(n.code,{children:"IReadOnlyCollection<>"}),")"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"LazyWrapper"}),": Used to resolve services ",(0,s.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#lazy",children:"wrapped"})," in ",(0,s.jsx)(n.code,{children:"Lazy<>"}),"."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"FuncWrapper"}),": Used to resolve services ",(0,s.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#delegate",children:"wrapped"})," in a ",(0,s.jsx)(n.code,{children:"Delegate"})," that has a non-void return type like ",(0,s.jsx)(n.code,{children:"Func<>"}),"."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"MetadataWrapper"}),": Used to resolve services ",(0,s.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#metadata--tuple",children:"wrapped"})," in ",(0,s.jsx)(n.code,{children:"ValueTuple<,>"}),", ",(0,s.jsx)(n.code,{children:"Tuple<,>"}),", or ",(0,s.jsx)(n.code,{children:"Metadata<,>"}),"."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"KeyValueWrapper"}),": Used to resolve services ",(0,s.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#keyvaluepair--readonlykeyvalue",children:"wrapped"})," in ",(0,s.jsx)(n.code,{children:"KeyValuePair<,>"})," or ",(0,s.jsx)(n.code,{children:"ReadOnlyKeyValue<,>"}),"."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"ServiceProviderResolver"}),": Used to resolve the actual scope as ",(0,s.jsx)(n.code,{children:"IServiceProvider"})," when no other implementation is registered."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"OptionalValueResolver"}),": Used to resolve optional parameters."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"DefaultValueResolver"}),": Used to resolve default values."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"ParentContainerResolver"}),": Used to resolve services that are only registered in one of the parent containers."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"UnknownTypeResolver"}),": Used to resolve services that are not registered into the container."]}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"wrappers",children:"Wrappers"}),"\n",(0,s.jsxs)(n.p,{children:["Stashbox can implicitly wrap your services into different data structures. All functionalities covered in the ",(0,s.jsx)(n.a,{href:"/docs/guides/service-resolution",children:"service resolution"})," are applied to the wrappers. Every wrapper request starts as a standard resolution; only the result is wrapped in the requested structure."]}),"\n",(0,s.jsxs)(i.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.h3,{id:"enumerable",children:"Enumerable"}),(0,s.jsxs)(n.p,{children:["Stashbox can compose a collection from each implementation registered to a ",(0,s.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),". The requested type can be wrapped by any of the collection interfaces that a .NET ",(0,s.jsx)(n.code,{children:"Array"})," implements."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"IJob[] jobs = container.Resolve();\nIEnumerable jobs = container.Resolve>();\nIList jobs = container.Resolve>();\nICollection jobs = container.Resolve>();\nIReadOnlyList jobs = container.Resolve>();\nIReadOnlyCollection jobs = container.Resolve>();\n"})})})]}),"\n",(0,s.jsxs)(i.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.h3,{id:"lazy",children:"Lazy"}),(0,s.jsxs)(n.p,{children:["When requesting ",(0,s.jsx)(n.code,{children:"Lazy<>"}),", the container implicitly constructs a new ",(0,s.jsx)(n.code,{children:"Lazy<>"})," instance with a factory delegate as its constructor argument used to instantiate the underlying service."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"container.Register();\n\n// new Lazy(() => new DbBackup())\nLazy lazyJob = container.Resolve>();\nIJob job = lazyJob.Value;\n"})})})]}),"\n",(0,s.jsxs)(i.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.h3,{id:"delegate",children:"Delegate"}),(0,s.jsxs)(n.p,{children:["When requesting a ",(0,s.jsx)(n.code,{children:"Delegate"}),", the container implicitly creates a factory used to instantiate the underlying service."]}),(0,s.jsxs)(n.p,{children:["It's possible to request a delegate that expects some or all of the dependencies as delegate parameters.\nParameters are used for sub-dependencies as well, like: ",(0,s.jsx)(n.code,{children:"(arg) => new A(new B(arg))"})]}),(0,s.jsx)(n.p,{children:"When a dependency is not available as a parameter, it will be resolved from the container directly."})]}),(0,s.jsx)("div",{children:(0,s.jsxs)(a.A,{children:[(0,s.jsx)(o.A,{value:"Func",label:"Func",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'container.Register();\n\n// (conn, logger) => new DbBackup(conn, logger)\nFunc funcOfJob = container\n .Resolve>();\n \nIJob job = funcOfJob(config["connectionString"], new ConsoleLogger());\n'})})}),(0,s.jsx)(o.A,{value:"Custom delegate",label:"Custom delegate",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'private delegate IJob JobFactory(string connectionString, ILogger logger);\n\ncontainer.Register();\n\nvar jobDelegate = container.Resolve();\nIJob job = jobDelegate(config["connectionString"], new ConsoleLogger());\n'})})})]})})]}),"\n",(0,s.jsxs)(i.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.h3,{id:"metadata--tuple",children:"Metadata & Tuple"}),(0,s.jsxs)(n.p,{children:["With the ",(0,s.jsx)(n.code,{children:".WithMetadata()"})," registration option, you can attach additional information to a service.\nTo gather this information, you can request the service wrapped in either ",(0,s.jsx)(n.code,{children:"Metadata<,>"}),", ",(0,s.jsx)(n.code,{children:"ValueTuple<,>"}),", or ",(0,s.jsx)(n.code,{children:"Tuple<,>"}),"."]}),(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"Metadata<,>"})," is a type from the ",(0,s.jsx)(n.code,{children:"Stashbox"})," package, so you might prefer using ",(0,s.jsx)(n.code,{children:"ValueTuple<,>"})," or ",(0,s.jsx)(n.code,{children:"Tuple<,>"})," if you want to avoid referencing Stashbox in certain parts of your project."]}),(0,s.jsxs)(n.p,{children:["You can also filter a collection of services by their metadata. Requesting ",(0,s.jsx)(n.code,{children:"IEnumerable>"})," will yield only those services that have the given type of metadata."]})]}),(0,s.jsx)("div",{children:(0,s.jsxs)(a.A,{children:[(0,s.jsx)(o.A,{value:"Single service",label:"Single service",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'container.Register(options => options\n .WithMetadata("connection-string-to-db"));\n\nvar jobWithConnectionString = container.Resolve>();\n// prints: "connection-string-to-db"\nConsole.WriteLine(jobWithConnectionString.Data);\n\nvar alsoJobWithConnectionString = container.Resolve>();\n// prints: "connection-string-to-db"\nConsole.WriteLine(alsoJobWithConnectionString.Item2);\n\nvar stillJobWithConnectionString = container.Resolve>();\n// prints: "connection-string-to-db"\nConsole.WriteLine(stillJobWithConnectionString.Item2);\n'})})}),(0,s.jsx)(o.A,{value:"Collection filtering",label:"Collection filtering",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'container.Register(options => options\n .WithMetadata("meta-1"));\ncontainer.Register(options => options\n .WithMetadata("meta-2"));\ncontainer.Register(options => options\n .WithMetadata(5));\n\n// the result is: [Service1, Service2]\nvar servicesWithStringMetadata = container.Resolve[]>();\n\n// the result is: [Service3]\nvar servicesWithIntMetadata = container.Resolve[]>();\n'})})})]})})]}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["Metadata can also be a complex type e.g., an ",(0,s.jsx)(n.code,{children:"IDictionary<,>"}),"."]})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["When no service found for a particular metadata type, the container throws a ",(0,s.jsx)(n.a,{href:"/docs/diagnostics/validation#resolution-validation",children:"ResolutionFailedException"}),". In case of an ",(0,s.jsx)(n.code,{children:"IEnumerable<>"})," request, an empty collection will be returned for a non-existing metadata."]})}),"\n",(0,s.jsxs)(i.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.h3,{id:"keyvaluepair--readonlykeyvalue",children:"KeyValuePair & ReadOnlyKeyValue"}),(0,s.jsxs)(n.p,{children:["With named registration, you can give your service unique identifiers. Requesting a service wrapped in a ",(0,s.jsx)(n.code,{children:"KeyValuePair"})," or ",(0,s.jsx)(n.code,{children:"ReadOnlyKeyValue"})," returns the requested service with its identifier as key."]}),(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"ReadOnlyKeyValue<,>"})," is a type from the ",(0,s.jsx)(n.code,{children:"Stashbox"})," package, so you might prefer using ",(0,s.jsx)(n.code,{children:"KeyValuePair<,>"})," if you want to avoid referencing Stashbox in certain parts of your project."]}),(0,s.jsxs)(n.p,{children:["Requesting an ",(0,s.jsx)(n.code,{children:"IEnumerable>"})," will return all services of the requested type along their identifiers. When a service don't have an identifier the ",(0,s.jsx)(n.code,{children:"Key"})," will be set to ",(0,s.jsx)(n.code,{children:"null"}),"."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'container.Register("FirstServiceId");\ncontainer.Register("SecondServiceId");\ncontainer.Register();\n\nvar serviceKeyValue1 = container\n .Resolve>("FirstServiceId");\n// prints: "FirstServiceId"\nConsole.WriteLine(serviceKeyValue1.Key);\n\nvar serviceKeyValue2 = container\n .Resolve>("SecondServiceId");\n// prints: "SecondServiceId"\nConsole.WriteLine(serviceKeyValue2.Key);\n\n// ["FirstServiceId": Service1, "SecondServiceId": Service2, null: Service3 ]\nvar servicesWithKeys = container.Resolve[]>();\n'})})})]}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["Wrappers can be composed e.g., ",(0,s.jsx)(n.code,{children:"IEnumerable, string>>>"}),"."]})}),"\n",(0,s.jsx)(n.h2,{id:"user-defined-wrappers--resolvers",children:"User-defined wrappers & resolvers"}),"\n",(0,s.jsxs)(n.p,{children:["You can add support for more wrapper types by implementing the ",(0,s.jsx)(n.code,{children:"IServiceWrapper"})," interface."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"class CustomWrapper : IServiceWrapper\n{\n // this method is supposed to generate the expression for the given wrapper's \n // instantiation when it's selected by the container to resolve the actual service.\n public Expression WrapExpression(\n TypeInformation originalTypeInformation, \n TypeInformation wrappedTypeInformation, \n ServiceContext serviceContext)\n {\n // produce the expression for the wrapper.\n }\n\n // this method is called by the container to determine whether a \n // given requested type is wrapped by a supported wrapper type.\n public bool TryUnWrap(Type type, out Type unWrappedType)\n {\n // this is just a reference implementation of \n // un-wrapping a service from a given wrapper.\n if (!CanUnWrapServiceType(type))\n {\n unWrappedType = typeof(object);\n return false;\n }\n\n unWrappedType = UnWrapServiceType(type);\n return true;\n }\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:["You can extend the functionality of the container by implementing the ",(0,s.jsx)(n.code,{children:"IServiceResolver"})," interface."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"class CustomResolver : IServiceResolver\n{\n // called to generate the expression for the given service\n // when this resolver is selected (through CanUseForResolution()) \n // to fulfill the request.\n public ServiceContext GetExpression(\n IResolutionStrategy resolutionStrategy,\n TypeInformation typeInfo,\n ResolutionContext resolutionContext)\n {\n var expression = GenerateExpression(); // resolution expression generation.\n return expression.AsServiceContext();\n }\n\n public bool CanUseForResolution(\n TypeInformation typeInfo,\n ResolutionContext resolutionContext)\n {\n\t // the predicate that determines whether the resolver \n // is able to resolve the requested service or not.\n return IsUsableFor(typeInfo);\n }\n}\n"})}),"\n",(0,s.jsx)(n.p,{children:"Then you can register your custom wrapper or resolver like this:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"container.RegisterResolver(new CustomWrapper());\ncontainer.RegisterResolver(new CustomResolver());\n"})}),"\n",(0,s.jsx)(n.h2,{id:"visiting-order",children:"Visiting order"}),"\n",(0,s.jsx)(n.p,{children:"Stashbox visits the wrappers and resolvers in the following order to satisfy the actual resolution request:"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"EnumerableWrapper"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"LazyWrapper"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"FuncWrapper"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"MetadataWrapper"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"KeyValueWrapper"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.strong,{children:"Custom, user-defined wrappers & resolvers"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"ServiceProviderResolver"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"OptionalValueResolver"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"DefaultValueResolver"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"ParentContainerResolver"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"UnknownTypeResolver"})}),"\n"]})]})}function v(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}},9365:(e,n,r)=>{r.d(n,{A:()=>a});r(6540);var s=r(870);const t={tabItem:"tabItem_Ymn6"};var i=r(4848);function a(e){let{children:n,hidden:r,className:a}=e;return(0,i.jsx)("div",{role:"tabpanel",className:(0,s.A)(t.tabItem,a),hidden:r,children:n})}},1470:(e,n,r)=>{r.d(n,{A:()=>I});var s=r(6540),t=r(870),i=r(3104),a=r(6347),o=r(205),l=r(7485),c=r(1682),d=r(9466);function u(e){return s.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,s.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function p(e){const{values:n,children:r}=e;return(0,s.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:r,attributes:s,default:t}}=e;return{value:n,label:r,attributes:s,default:t}}))}(r);return function(e){const n=(0,c.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,r])}function h(e){let{value:n,tabValues:r}=e;return r.some((e=>e.value===n))}function v(e){let{queryString:n=!1,groupId:r}=e;const t=(0,a.W6)(),i=function(e){let{queryString:n=!1,groupId:r}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!r)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return r??null}({queryString:n,groupId:r});return[(0,l.aZ)(i),(0,s.useCallback)((e=>{if(!i)return;const n=new URLSearchParams(t.location.search);n.set(i,e),t.replace({...t.location,search:n.toString()})}),[i,t])]}function x(e){const{defaultValue:n,queryString:r=!1,groupId:t}=e,i=p(e),[a,l]=(0,s.useState)((()=>function(e){let{defaultValue:n,tabValues:r}=e;if(0===r.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!h({value:n,tabValues:r}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${r.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const s=r.find((e=>e.default))??r[0];if(!s)throw new Error("Unexpected error: 0 tabValues");return s.value}({defaultValue:n,tabValues:i}))),[c,u]=v({queryString:r,groupId:t}),[x,j]=function(e){let{groupId:n}=e;const r=function(e){return e?`docusaurus.tab.${e}`:null}(n),[t,i]=(0,d.Dv)(r);return[t,(0,s.useCallback)((e=>{r&&i.set(e)}),[r,i])]}({groupId:t}),b=(()=>{const e=c??x;return h({value:e,tabValues:i})?e:null})();(0,o.A)((()=>{b&&l(b)}),[b]);return{selectedValue:a,selectValue:(0,s.useCallback)((e=>{if(!h({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);l(e),u(e),j(e)}),[u,j,i]),tabValues:i}}var j=r(2303);const b={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var g=r(4848);function f(e){let{className:n,block:r,selectedValue:s,selectValue:a,tabValues:o}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,i.a_)(),d=e=>{const n=e.currentTarget,r=l.indexOf(n),t=o[r].value;t!==s&&(c(n),a(t))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const r=l.indexOf(e.currentTarget)+1;n=l[r]??l[0];break}case"ArrowLeft":{const r=l.indexOf(e.currentTarget)-1;n=l[r]??l[l.length-1];break}}n?.focus()};return(0,g.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,t.A)("tabs",{"tabs--block":r},n),children:o.map((e=>{let{value:n,label:r,attributes:i}=e;return(0,g.jsx)("li",{role:"tab",tabIndex:s===n?0:-1,"aria-selected":s===n,ref:e=>l.push(e),onKeyDown:u,onClick:d,...i,className:(0,t.A)("tabs__item",b.tabItem,i?.className,{"tabs__item--active":s===n}),children:r??n},n)}))})}function m(e){let{lazy:n,children:r,selectedValue:t}=e;const i=(Array.isArray(r)?r:[r]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===t));return e?(0,s.cloneElement)(e,{className:"margin-top--md"}):null}return(0,g.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,s.cloneElement)(e,{key:n,hidden:e.props.value!==t})))})}function y(e){const n=x(e);return(0,g.jsxs)("div",{className:(0,t.A)("tabs-container",b.tabList),children:[(0,g.jsx)(f,{...e,...n}),(0,g.jsx)(m,{...e,...n})]})}function I(e){const n=(0,j.A)();return(0,g.jsx)(y,{...e,children:u(e.children)},String(n))}},7470:(e,n,r)=>{r.d(n,{A:()=>a});var s=r(6540);const t={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var i=r(4848);function a(e){let{children:n}=e,r=s.Children.toArray(n).filter((e=>e));return(0,i.jsxs)("div",{className:t.codeDescContainer,children:[(0,i.jsx)("div",{className:t.desc,children:r[0]}),(0,i.jsx)("div",{className:t.example,children:r[1]})]})}},8453:(e,n,r)=>{r.d(n,{R:()=>a,x:()=>o});var s=r(6540);const t={},i=s.createContext(t);function a(e){const n=s.useContext(i);return s.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:a(e.components),s.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[429],{9267:(e,n,r)=>{r.r(n),r.d(n,{assets:()=>u,contentTitle:()=>c,default:()=>v,frontMatter:()=>l,metadata:()=>d,toc:()=>p});var s=r(4848),t=r(8453),i=r(7470),a=r(1470),o=r(9365);const l={},c="Wrappers & resolvers",d={id:"advanced/wrappers-resolvers",title:"Wrappers & resolvers",description:"Stashbox uses so-called Wrapper and Resolver implementations to handle special resolution requests that none of the service registrations can fulfill. Functionalities like wrapper and unknown type resolution, cross-container requests, optional and default value injection are all built with resolvers.",source:"@site/docs/advanced/wrappers-resolvers.md",sourceDirName:"advanced",slug:"/advanced/wrappers-resolvers",permalink:"/stashbox/docs/advanced/wrappers-resolvers",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/advanced/wrappers-resolvers.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Decorators",permalink:"/stashbox/docs/advanced/decorators"},next:{title:"Child containers",permalink:"/stashbox/docs/advanced/child-containers"}},u={},p=[{value:"Pre-defined wrappers & resolvers",id:"pre-defined-wrappers--resolvers",level:2},{value:"Wrappers",id:"wrappers",level:2},{value:"Enumerable",id:"enumerable",level:3},{value:"Lazy",id:"lazy",level:3},{value:"Delegate",id:"delegate",level:3},{value:"Metadata & Tuple",id:"metadata--tuple",level:3},{value:"KeyValuePair & ReadOnlyKeyValue",id:"keyvaluepair--readonlykeyvalue",level:3},{value:"User-defined wrappers & resolvers",id:"user-defined-wrappers--resolvers",level:2},{value:"Visiting order",id:"visiting-order",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.h1,{id:"wrappers--resolvers",children:"Wrappers & resolvers"}),"\n",(0,s.jsxs)(n.p,{children:["Stashbox uses so-called ",(0,s.jsx)(n.em,{children:"Wrapper"})," and ",(0,s.jsx)(n.em,{children:"Resolver"})," implementations to handle special resolution requests that none of the ",(0,s.jsx)(n.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"service registrations"})," can fulfill. Functionalities like ",(0,s.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#wrappers",children:"wrapper"})," and ",(0,s.jsx)(n.a,{href:"/docs/advanced/special-resolution-cases#unknown-type-resolution",children:"unknown type"})," resolution, ",(0,s.jsx)(n.a,{href:"/docs/advanced/child-containers",children:"cross-container requests"}),", ",(0,s.jsx)(n.a,{href:"/docs/advanced/special-resolution-cases#optional-value-injection",children:"optional"})," and ",(0,s.jsx)(n.a,{href:"/docs/advanced/special-resolution-cases#default-value-injection",children:"default value"})," injection are all built with resolvers."]}),"\n",(0,s.jsx)(n.h2,{id:"pre-defined-wrappers--resolvers",children:"Pre-defined wrappers & resolvers"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"EnumerableWrapper"}),": Used to resolve a collection of services wrapped in one of the collection interfaces that a .NET ",(0,s.jsx)(n.code,{children:"Array"})," implements. (",(0,s.jsx)(n.code,{children:"IEnumerable<>"}),", ",(0,s.jsx)(n.code,{children:"IList<>"}),", ",(0,s.jsx)(n.code,{children:"ICollection<>"}),", ",(0,s.jsx)(n.code,{children:"IReadOnlyList<>"}),", ",(0,s.jsx)(n.code,{children:"IReadOnlyCollection<>"}),")"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"LazyWrapper"}),": Used to resolve services ",(0,s.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#lazy",children:"wrapped"})," in ",(0,s.jsx)(n.code,{children:"Lazy<>"}),"."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"FuncWrapper"}),": Used to resolve services ",(0,s.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#delegate",children:"wrapped"})," in a ",(0,s.jsx)(n.code,{children:"Delegate"})," that has a non-void return type like ",(0,s.jsx)(n.code,{children:"Func<>"}),"."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"MetadataWrapper"}),": Used to resolve services ",(0,s.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#metadata--tuple",children:"wrapped"})," in ",(0,s.jsx)(n.code,{children:"ValueTuple<,>"}),", ",(0,s.jsx)(n.code,{children:"Tuple<,>"}),", or ",(0,s.jsx)(n.code,{children:"Metadata<,>"}),"."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"KeyValueWrapper"}),": Used to resolve services ",(0,s.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#keyvaluepair--readonlykeyvalue",children:"wrapped"})," in ",(0,s.jsx)(n.code,{children:"KeyValuePair<,>"})," or ",(0,s.jsx)(n.code,{children:"ReadOnlyKeyValue<,>"}),"."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"ServiceProviderResolver"}),": Used to resolve the actual scope as ",(0,s.jsx)(n.code,{children:"IServiceProvider"})," when no other implementation is registered."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"OptionalValueResolver"}),": Used to resolve optional parameters."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"DefaultValueResolver"}),": Used to resolve default values."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"ParentContainerResolver"}),": Used to resolve services that are only registered in one of the parent containers."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"UnknownTypeResolver"}),": Used to resolve services that are not registered into the container."]}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"wrappers",children:"Wrappers"}),"\n",(0,s.jsxs)(n.p,{children:["Stashbox can implicitly wrap your services into different data structures. All functionalities covered in the ",(0,s.jsx)(n.a,{href:"/docs/guides/service-resolution",children:"service resolution"})," are applied to the wrappers. Every wrapper request starts as a standard resolution; only the result is wrapped in the requested structure."]}),"\n",(0,s.jsxs)(i.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.h3,{id:"enumerable",children:"Enumerable"}),(0,s.jsxs)(n.p,{children:["Stashbox can compose a collection from each implementation registered to a ",(0,s.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),". The requested type can be wrapped by any of the collection interfaces that a .NET ",(0,s.jsx)(n.code,{children:"Array"})," implements."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"IJob[] jobs = container.Resolve();\nIEnumerable jobs = container.Resolve>();\nIList jobs = container.Resolve>();\nICollection jobs = container.Resolve>();\nIReadOnlyList jobs = container.Resolve>();\nIReadOnlyCollection jobs = container.Resolve>();\n"})})})]}),"\n",(0,s.jsxs)(i.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.h3,{id:"lazy",children:"Lazy"}),(0,s.jsxs)(n.p,{children:["When requesting ",(0,s.jsx)(n.code,{children:"Lazy<>"}),", the container implicitly constructs a new ",(0,s.jsx)(n.code,{children:"Lazy<>"})," instance with a factory delegate as its constructor argument used to instantiate the underlying service."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"container.Register();\n\n// new Lazy(() => new DbBackup())\nLazy lazyJob = container.Resolve>();\nIJob job = lazyJob.Value;\n"})})})]}),"\n",(0,s.jsxs)(i.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.h3,{id:"delegate",children:"Delegate"}),(0,s.jsxs)(n.p,{children:["When requesting a ",(0,s.jsx)(n.code,{children:"Delegate"}),", the container implicitly creates a factory used to instantiate the underlying service."]}),(0,s.jsxs)(n.p,{children:["It's possible to request a delegate that expects some or all of the dependencies as delegate parameters.\nParameters are used for sub-dependencies as well, like: ",(0,s.jsx)(n.code,{children:"(arg) => new A(new B(arg))"})]}),(0,s.jsx)(n.p,{children:"When a dependency is not available as a parameter, it will be resolved from the container directly."})]}),(0,s.jsx)("div",{children:(0,s.jsxs)(a.A,{children:[(0,s.jsx)(o.A,{value:"Func",label:"Func",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'container.Register();\n\n// (conn, logger) => new DbBackup(conn, logger)\nFunc funcOfJob = container\n .Resolve>();\n \nIJob job = funcOfJob(config["connectionString"], new ConsoleLogger());\n'})})}),(0,s.jsx)(o.A,{value:"Custom delegate",label:"Custom delegate",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'private delegate IJob JobFactory(string connectionString, ILogger logger);\n\ncontainer.Register();\n\nvar jobDelegate = container.Resolve();\nIJob job = jobDelegate(config["connectionString"], new ConsoleLogger());\n'})})})]})})]}),"\n",(0,s.jsxs)(i.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.h3,{id:"metadata--tuple",children:"Metadata & Tuple"}),(0,s.jsxs)(n.p,{children:["With the ",(0,s.jsx)(n.code,{children:".WithMetadata()"})," registration option, you can attach additional information to a service.\nTo gather this information, you can request the service wrapped in either ",(0,s.jsx)(n.code,{children:"Metadata<,>"}),", ",(0,s.jsx)(n.code,{children:"ValueTuple<,>"}),", or ",(0,s.jsx)(n.code,{children:"Tuple<,>"}),"."]}),(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"Metadata<,>"})," is a type from the ",(0,s.jsx)(n.code,{children:"Stashbox"})," package, so you might prefer using ",(0,s.jsx)(n.code,{children:"ValueTuple<,>"})," or ",(0,s.jsx)(n.code,{children:"Tuple<,>"})," if you want to avoid referencing Stashbox in certain parts of your project."]}),(0,s.jsxs)(n.p,{children:["You can also filter a collection of services by their metadata. Requesting ",(0,s.jsx)(n.code,{children:"IEnumerable>"})," will yield only those services that have the given type of metadata."]})]}),(0,s.jsx)("div",{children:(0,s.jsxs)(a.A,{children:[(0,s.jsx)(o.A,{value:"Single service",label:"Single service",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'container.Register(options => options\n .WithMetadata("connection-string-to-db"));\n\nvar jobWithConnectionString = container.Resolve>();\n// prints: "connection-string-to-db"\nConsole.WriteLine(jobWithConnectionString.Data);\n\nvar alsoJobWithConnectionString = container.Resolve>();\n// prints: "connection-string-to-db"\nConsole.WriteLine(alsoJobWithConnectionString.Item2);\n\nvar stillJobWithConnectionString = container.Resolve>();\n// prints: "connection-string-to-db"\nConsole.WriteLine(stillJobWithConnectionString.Item2);\n'})})}),(0,s.jsx)(o.A,{value:"Collection filtering",label:"Collection filtering",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'container.Register(options => options\n .WithMetadata("meta-1"));\ncontainer.Register(options => options\n .WithMetadata("meta-2"));\ncontainer.Register(options => options\n .WithMetadata(5));\n\n// the result is: [Service1, Service2]\nvar servicesWithStringMetadata = container.Resolve[]>();\n\n// the result is: [Service3]\nvar servicesWithIntMetadata = container.Resolve[]>();\n'})})})]})})]}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["Metadata can also be a complex type e.g., an ",(0,s.jsx)(n.code,{children:"IDictionary<,>"}),"."]})}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["When no service found for a particular metadata type, the container throws a ",(0,s.jsx)(n.a,{href:"/docs/diagnostics/validation#resolution-validation",children:"ResolutionFailedException"}),". In case of an ",(0,s.jsx)(n.code,{children:"IEnumerable<>"})," request, an empty collection will be returned for a non-existing metadata."]})}),"\n",(0,s.jsxs)(i.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(n.h3,{id:"keyvaluepair--readonlykeyvalue",children:"KeyValuePair & ReadOnlyKeyValue"}),(0,s.jsxs)(n.p,{children:["With named registration, you can give your service unique identifiers. Requesting a service wrapped in a ",(0,s.jsx)(n.code,{children:"KeyValuePair"})," or ",(0,s.jsx)(n.code,{children:"ReadOnlyKeyValue"})," returns the requested service with its identifier as key."]}),(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"ReadOnlyKeyValue<,>"})," is a type from the ",(0,s.jsx)(n.code,{children:"Stashbox"})," package, so you might prefer using ",(0,s.jsx)(n.code,{children:"KeyValuePair<,>"})," if you want to avoid referencing Stashbox in certain parts of your project."]}),(0,s.jsxs)(n.p,{children:["Requesting an ",(0,s.jsx)(n.code,{children:"IEnumerable>"})," will return all services of the requested type along their identifiers. When a service don't have an identifier the ",(0,s.jsx)(n.code,{children:"Key"})," will be set to ",(0,s.jsx)(n.code,{children:"null"}),"."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'container.Register("FirstServiceId");\ncontainer.Register("SecondServiceId");\ncontainer.Register();\n\nvar serviceKeyValue1 = container\n .Resolve>("FirstServiceId");\n// prints: "FirstServiceId"\nConsole.WriteLine(serviceKeyValue1.Key);\n\nvar serviceKeyValue2 = container\n .Resolve>("SecondServiceId");\n// prints: "SecondServiceId"\nConsole.WriteLine(serviceKeyValue2.Key);\n\n// ["FirstServiceId": Service1, "SecondServiceId": Service2, null: Service3 ]\nvar servicesWithKeys = container.Resolve[]>();\n'})})})]}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["Wrappers can be composed e.g., ",(0,s.jsx)(n.code,{children:"IEnumerable, string>>>"}),"."]})}),"\n",(0,s.jsx)(n.h2,{id:"user-defined-wrappers--resolvers",children:"User-defined wrappers & resolvers"}),"\n",(0,s.jsxs)(n.p,{children:["You can add support for more wrapper types by implementing the ",(0,s.jsx)(n.code,{children:"IServiceWrapper"})," interface."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"class CustomWrapper : IServiceWrapper\n{\n // this method is supposed to generate the expression for the given wrapper's \n // instantiation when it's selected by the container to resolve the actual service.\n public Expression WrapExpression(\n TypeInformation originalTypeInformation, \n TypeInformation wrappedTypeInformation, \n ServiceContext serviceContext)\n {\n // produce the expression for the wrapper.\n }\n\n // this method is called by the container to determine whether a \n // given requested type is wrapped by a supported wrapper type.\n public bool TryUnWrap(Type type, out Type unWrappedType)\n {\n // this is just a reference implementation of \n // un-wrapping a service from a given wrapper.\n if (!CanUnWrapServiceType(type))\n {\n unWrappedType = typeof(object);\n return false;\n }\n\n unWrappedType = UnWrapServiceType(type);\n return true;\n }\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:["You can extend the functionality of the container by implementing the ",(0,s.jsx)(n.code,{children:"IServiceResolver"})," interface."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"class CustomResolver : IServiceResolver\n{\n // called to generate the expression for the given service\n // when this resolver is selected (through CanUseForResolution()) \n // to fulfill the request.\n public ServiceContext GetExpression(\n IResolutionStrategy resolutionStrategy,\n TypeInformation typeInfo,\n ResolutionContext resolutionContext)\n {\n var expression = GenerateExpression(); // resolution expression generation.\n return expression.AsServiceContext();\n }\n\n public bool CanUseForResolution(\n TypeInformation typeInfo,\n ResolutionContext resolutionContext)\n {\n\t // the predicate that determines whether the resolver \n // is able to resolve the requested service or not.\n return IsUsableFor(typeInfo);\n }\n}\n"})}),"\n",(0,s.jsx)(n.p,{children:"Then you can register your custom wrapper or resolver like this:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"container.RegisterResolver(new CustomWrapper());\ncontainer.RegisterResolver(new CustomResolver());\n"})}),"\n",(0,s.jsx)(n.h2,{id:"visiting-order",children:"Visiting order"}),"\n",(0,s.jsx)(n.p,{children:"Stashbox visits the wrappers and resolvers in the following order to satisfy the actual resolution request:"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"EnumerableWrapper"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"LazyWrapper"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"FuncWrapper"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"MetadataWrapper"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"KeyValueWrapper"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.strong,{children:"Custom, user-defined wrappers & resolvers"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"ServiceProviderResolver"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"OptionalValueResolver"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"DefaultValueResolver"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"ParentContainerResolver"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"UnknownTypeResolver"})}),"\n"]})]})}function v(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}},9365:(e,n,r)=>{r.d(n,{A:()=>a});r(6540);var s=r(870);const t={tabItem:"tabItem_Ymn6"};var i=r(4848);function a(e){let{children:n,hidden:r,className:a}=e;return(0,i.jsx)("div",{role:"tabpanel",className:(0,s.A)(t.tabItem,a),hidden:r,children:n})}},1470:(e,n,r)=>{r.d(n,{A:()=>I});var s=r(6540),t=r(870),i=r(3104),a=r(6347),o=r(205),l=r(7485),c=r(1682),d=r(9466);function u(e){return s.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,s.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function p(e){const{values:n,children:r}=e;return(0,s.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:r,attributes:s,default:t}}=e;return{value:n,label:r,attributes:s,default:t}}))}(r);return function(e){const n=(0,c.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,r])}function h(e){let{value:n,tabValues:r}=e;return r.some((e=>e.value===n))}function v(e){let{queryString:n=!1,groupId:r}=e;const t=(0,a.W6)(),i=function(e){let{queryString:n=!1,groupId:r}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!r)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return r??null}({queryString:n,groupId:r});return[(0,l.aZ)(i),(0,s.useCallback)((e=>{if(!i)return;const n=new URLSearchParams(t.location.search);n.set(i,e),t.replace({...t.location,search:n.toString()})}),[i,t])]}function x(e){const{defaultValue:n,queryString:r=!1,groupId:t}=e,i=p(e),[a,l]=(0,s.useState)((()=>function(e){let{defaultValue:n,tabValues:r}=e;if(0===r.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!h({value:n,tabValues:r}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${r.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const s=r.find((e=>e.default))??r[0];if(!s)throw new Error("Unexpected error: 0 tabValues");return s.value}({defaultValue:n,tabValues:i}))),[c,u]=v({queryString:r,groupId:t}),[x,j]=function(e){let{groupId:n}=e;const r=function(e){return e?`docusaurus.tab.${e}`:null}(n),[t,i]=(0,d.Dv)(r);return[t,(0,s.useCallback)((e=>{r&&i.set(e)}),[r,i])]}({groupId:t}),b=(()=>{const e=c??x;return h({value:e,tabValues:i})?e:null})();(0,o.A)((()=>{b&&l(b)}),[b]);return{selectedValue:a,selectValue:(0,s.useCallback)((e=>{if(!h({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);l(e),u(e),j(e)}),[u,j,i]),tabValues:i}}var j=r(2303);const b={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var g=r(4848);function f(e){let{className:n,block:r,selectedValue:s,selectValue:a,tabValues:o}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,i.a_)(),d=e=>{const n=e.currentTarget,r=l.indexOf(n),t=o[r].value;t!==s&&(c(n),a(t))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const r=l.indexOf(e.currentTarget)+1;n=l[r]??l[0];break}case"ArrowLeft":{const r=l.indexOf(e.currentTarget)-1;n=l[r]??l[l.length-1];break}}n?.focus()};return(0,g.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,t.A)("tabs",{"tabs--block":r},n),children:o.map((e=>{let{value:n,label:r,attributes:i}=e;return(0,g.jsx)("li",{role:"tab",tabIndex:s===n?0:-1,"aria-selected":s===n,ref:e=>l.push(e),onKeyDown:u,onClick:d,...i,className:(0,t.A)("tabs__item",b.tabItem,i?.className,{"tabs__item--active":s===n}),children:r??n},n)}))})}function m(e){let{lazy:n,children:r,selectedValue:t}=e;const i=(Array.isArray(r)?r:[r]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===t));return e?(0,s.cloneElement)(e,{className:"margin-top--md"}):null}return(0,g.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,s.cloneElement)(e,{key:n,hidden:e.props.value!==t})))})}function y(e){const n=x(e);return(0,g.jsxs)("div",{className:(0,t.A)("tabs-container",b.tabList),children:[(0,g.jsx)(f,{...e,...n}),(0,g.jsx)(m,{...e,...n})]})}function I(e){const n=(0,j.A)();return(0,g.jsx)(y,{...e,children:u(e.children)},String(n))}},7470:(e,n,r)=>{r.d(n,{A:()=>a});var s=r(6540);const t={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var i=r(4848);function a(e){let{children:n}=e,r=s.Children.toArray(n).filter((e=>e));return(0,i.jsxs)("div",{className:t.codeDescContainer,children:[(0,i.jsx)("div",{className:t.desc,children:r[0]}),(0,i.jsx)("div",{className:t.example,children:r[1]})]})}},8453:(e,n,r)=>{r.d(n,{R:()=>a,x:()=>o});var s=r(6540);const t={},i=s.createContext(t);function a(e){const n=s.useContext(i);return s.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:a(e.components),s.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/6d4ed487.43c43c69.js b/assets/js/6d4ed487.af545583.js similarity index 99% rename from assets/js/6d4ed487.43c43c69.js rename to assets/js/6d4ed487.af545583.js index 086cd3dc..7928d241 100644 --- a/assets/js/6d4ed487.43c43c69.js +++ b/assets/js/6d4ed487.af545583.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[747],{5151:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>v,contentTitle:()=>d,default:()=>p,frontMatter:()=>o,metadata:()=>c,toc:()=>u});var r=t(4848),a=t(8453),i=t(7470),s=t(1470),l=t(9365);const o={},d="Generics",c={id:"advanced/generics",title:"Generics",description:"This section is about how Stashbox handles various usage scenarios that involve .NET Generic types. Including the registration of open-generic and closed-generic types, generic decorators, conditions based on generic constraints, and variance.",source:"@site/docs/advanced/generics.md",sourceDirName:"advanced",slug:"/advanced/generics",permalink:"/stashbox/docs/advanced/generics",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/advanced/generics.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Container configuration",permalink:"/stashbox/docs/configuration/container-configuration"},next:{title:"Decorators",permalink:"/stashbox/docs/advanced/decorators"}},v={},u=[{value:"Closed-generics",id:"closed-generics",level:2},{value:"Open-generics",id:"open-generics",level:2},{value:"Generic constraints",id:"generic-constraints",level:2},{value:"Variance",id:"variance",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,a.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h1,{id:"generics",children:"Generics"}),"\n",(0,r.jsxs)(n.p,{children:["This section is about how Stashbox handles various usage scenarios that involve .NET Generic types. Including the registration of open-generic and closed-generic types, ",(0,r.jsx)(n.a,{href:"/docs/advanced/decorators#generic-decorators",children:"generic decorators"}),", conditions based on generic constraints, and variance."]}),"\n",(0,r.jsx)(n.h2,{id:"closed-generics",children:"Closed-generics"}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsxs)("div",{children:[(0,r.jsx)(n.p,{children:"The registration of a closed-generic type does not differ from registering a simple non-generic service."}),(0,r.jsxs)(n.p,{children:["You have all options available that you saw at the ",(0,r.jsx)(n.a,{href:"/docs/guides/basics",children:"basic"})," and ",(0,r.jsx)(n.a,{href:"/docs/guides/advanced-registration",children:"advanced registration"})," flows."]})]}),(0,r.jsx)("div",{children:(0,r.jsxs)(s.A,{groupId:"generic-runtime-apis",children:[(0,r.jsx)(l.A,{value:"Generic API",label:"Generic API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"container.Register, UserValidator>();\nIValidator validator = container.Resolve>();\n"})})}),(0,r.jsx)(l.A,{value:"Runtime type API",label:"Runtime type API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"container.Register(typeof(IValidator), typeof(UserValidator));\nobject validator = container.Resolve(typeof(IValidator));\n"})})})]})})]}),"\n",(0,r.jsx)(n.h2,{id:"open-generics",children:"Open-generics"}),"\n",(0,r.jsxs)(n.p,{children:["The registration of an open-generic type differs from registering a closed-generic one as C# doesn't allow the usage of open-generic types in generic method parameters. We have to get a runtime type from the open-generic type first with ",(0,r.jsx)(n.code,{children:"typeof()"}),"."]}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsx)("div",{children:(0,r.jsx)(n.p,{children:"Open-generic types could help in such scenarios where you have generic interface-implementation pairs with numerous generic parameter variations. The registration of those different versions would look like this:"})}),(0,r.jsx)("div",{children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"container.Register, Validator>();\ncontainer.Register, Validator>();\ncontainer.Register, Validator>();\n// and so on...\n"})})})]}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsx)("div",{children:(0,r.jsxs)(n.p,{children:["Rather than doing that, you can register your type's generic definition and let Stashbox bind the type parameters for you. When a matching closed ",(0,r.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," is requested, the container will construct an equivalent closed-generic implementation."]})}),(0,r.jsx)("div",{children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"container.Register(typeof(IValidator<>), typeof(Validator<>));\n// Validator will be returned.\nIValidator userValidator = container.Resolve>();\n// Validator will be returned.\nIValidator roleValidator = container.Resolve>();\n"})})})]}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsx)("div",{children:(0,r.jsx)(n.p,{children:"A registered closed-generic type always has priority over an open-generic type at service selection."})}),(0,r.jsx)("div",{children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"container.Register, UserValidator>();\ncontainer.Register(typeof(IValidator<>), typeof(Validator<>));\n// UserValidator will be returned.\nIValidator validator = container.Resolve>();\n"})})})]}),"\n",(0,r.jsx)(n.h2,{id:"generic-constraints",children:"Generic constraints"}),"\n",(0,r.jsxs)(n.p,{children:["In the following examples, you can see how the container handles generic constraints during service resolution. Constraints can be used for ",(0,r.jsx)(n.a,{href:"/docs/guides/service-resolution#conditional-resolution",children:"conditional resolution"})," including collection filters."]}),"\n",(0,r.jsxs)(s.A,{children:[(0,r.jsxs)(l.A,{value:"Conditional resolution",label:"Conditional resolution",children:[(0,r.jsxs)(n.p,{children:["The container chooses ",(0,r.jsx)(n.code,{children:"UpdatedEventHandler"})," because it is the only one that has a constraint satisfied by the requested ",(0,r.jsx)(n.code,{children:"UserUpdatedEvent"})," generic parameter as it's implementing ",(0,r.jsx)(n.code,{children:"IUpdatedEvent"}),"."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"interface IEventHandler { }\n\n// event interfaces\ninterface IUpdatedEvent { }\ninterface ICreatedEvent { }\n\n// event handlers\nclass UpdatedEventHandler : IEventHandler where TEvent : IUpdatedEvent { }\nclass CreatedEventHandler : IEventHandler where TEvent : ICreatedEvent { }\n\n// event implementation\nclass UserUpdatedEvent : IUpdatedEvent { }\n\nusing var container = new StashboxContainer();\n\ncontainer.RegisterTypesAs(typeof(IEventHandler<>), new[] \n { \n typeof(UpdateEventHandler<>), \n typeof(CreateEventHandler<>) \n });\n\n// eventHandler will be UpdatedEventHandler\nIEventHandler eventHandler = container.Resolve>();\n"})})]}),(0,r.jsxs)(l.A,{value:"Collection filter",label:"Collection filter",children:[(0,r.jsx)(n.p,{children:"This example shows how the container is filtering out those services from the returned collection that does not satisfy the given generic constraint needed to create the closed generic type."}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"interface IEventHandler { }\n\n// event interfaces\ninterface IUpdatedEvent { }\ninterface ICreatedEvent { }\n\n// event handlers\nclass UpdatedEventHandler : IEventHandler where TEvent : IUpdatedEvent { }\nclass CreatedEventHandler : IEventHandler where TEvent : ICreatedEvent { }\n\n// event implementation\nclass UserUpdatedEvent : IUpdatedEvent { }\n\nusing var container = new StashboxContainer();\n\ncontainer.RegisterTypesAs(typeof(IEventHandler<>), new[] \n { \n typeof(UpdateEventHandler<>), \n typeof(CreateEventHandler<>) \n });\n\n// eventHandlers will contain only UpdatedEventHandler\nIEnumerable> eventHandlers = container.ResolveAll>();\n"})})]})]}),"\n",(0,r.jsx)(n.h2,{id:"variance",children:"Variance"}),"\n",(0,r.jsxs)(n.p,{children:["Since .NET Framework 4.0, C# supports ",(0,r.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/",children:"covariance and contravariance"})," in generic interfaces and delegates and allows implicit conversion of generic type parameters. In this section, we'll focus on variance in generic interfaces."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/creating-variant-generic-interfaces",children:"Here"})," you can read more about how to create variant generic interfaces, and the following example will show how you can use them with Stashbox."]}),"\n",(0,r.jsxs)(s.A,{children:[(0,r.jsxs)(l.A,{value:"Contravariance",label:"Contravariance",children:[(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Contravariance"})," only allows argument types that are less derived than that defined by the generic parameters. You can declare a generic type parameter contravariant by using the ",(0,r.jsx)(n.code,{children:"in"})," keyword."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"// contravariant generic event handler interface\ninterface IEventHandler { } \n\n// event interfaces\ninterface IGeneralEvent { }\ninterface IUpdatedEvent : IGeneralEvent { }\n\n// event handlers\nclass GeneralEventHandler : IEventHandler { }\nclass UpdatedEventHandler : IEventHandler { }\n\ncontainer.Register, GeneralEventHandler>();\ncontainer.Register, UpdatedEventHandler>();\n\n// eventHandlers contain both GeneralEventHandler and UpdatedEventHandler\nIEnumerable> eventHandlers = container.ResolveAll>();\n"})}),(0,r.jsxs)(n.p,{children:["Despite the fact that only ",(0,r.jsx)(n.code,{children:"IEventHandler"})," implementations were requested, the result contains both ",(0,r.jsx)(n.code,{children:"GeneralEventHandler"})," and ",(0,r.jsx)(n.code,{children:"UpdatedEventHandler"}),". As ",(0,r.jsx)(n.code,{children:"TEvent"})," is declared ",(0,r.jsx)(n.strong,{children:"contravariant"})," with the ",(0,r.jsx)(n.code,{children:"in"})," keyword, and ",(0,r.jsx)(n.code,{children:"IGeneralEvent"})," is less derived than ",(0,r.jsx)(n.code,{children:"IUpdatedEvent"}),", ",(0,r.jsx)(n.code,{children:"IEventHandler"})," implementations can be part of ",(0,r.jsx)(n.code,{children:"IEventHandler"})," collections."]}),(0,r.jsxs)(n.p,{children:["If we request ",(0,r.jsx)(n.code,{children:"IEventHandler"}),", only ",(0,r.jsx)(n.code,{children:"GeneralEventHandler"})," would be returned, because ",(0,r.jsx)(n.code,{children:"IUpdatedEvent"})," is more derived, so ",(0,r.jsx)(n.code,{children:"IEventHandler"})," implementations are not fit into ",(0,r.jsx)(n.code,{children:"IEventHandler"})," collections."]})]}),(0,r.jsxs)(l.A,{value:"Covariance",label:"Covariance",children:[(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Covariance"})," only allows argument types that are more derived than that defined by the generic parameters. You can declare a generic type parameter covariant by using the ",(0,r.jsx)(n.code,{children:"out"})," keyword."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"// covariant generic event handler interface\ninterface IEventHandler { } \n\n// event interfaces\ninterface IGeneralEvent { }\ninterface IUpdatedEvent : IGeneralEvent { }\n\n// event handlers\nclass GeneralEventHandler : IEventHandler { }\nclass UpdatedEventHandler : IEventHandler { }\n\ncontainer.Register, GeneralEventHandler>();\ncontainer.Register, UpdatedEventHandler>();\n\n// eventHandlers contain both GeneralEventHandler and UpdatedEventHandler\nIEnumerable> eventHandlers = container.ResolveAll>();\n"})}),(0,r.jsxs)(n.p,{children:["Despite the fact that only ",(0,r.jsx)(n.code,{children:"IEventHandler"})," implementations were requested, the result contains both ",(0,r.jsx)(n.code,{children:"GeneralEventHandler"})," and ",(0,r.jsx)(n.code,{children:"UpdatedEventHandler"}),". As ",(0,r.jsx)(n.code,{children:"TEvent"})," is declared ",(0,r.jsx)(n.strong,{children:"covariant"})," with the ",(0,r.jsx)(n.code,{children:"out"})," keyword, and ",(0,r.jsx)(n.code,{children:"IUpdatedEvent"})," is more derived than ",(0,r.jsx)(n.code,{children:"IGeneralEvent"}),", ",(0,r.jsx)(n.code,{children:"IEventHandler"})," implementations can be part of ",(0,r.jsx)(n.code,{children:"IEventHandler"})," collections."]}),(0,r.jsxs)(n.p,{children:["If we request ",(0,r.jsx)(n.code,{children:"IEventHandler"}),", only ",(0,r.jsx)(n.code,{children:"UpdatedEventHandler"})," would be returned, because ",(0,r.jsx)(n.code,{children:"IGeneralEvent"})," is less derived, so ",(0,r.jsx)(n.code,{children:"IEventHandler"})," implementations are not fit into ",(0,r.jsx)(n.code,{children:"IEventHandler"})," collections."]})]})]}),"\n",(0,r.jsx)(n.admonition,{type:"info",children:(0,r.jsxs)(n.p,{children:["The check for variant generic types is enabled by default, but it can be turned off via a ",(0,r.jsx)(n.a,{href:"/docs/configuration/container-configuration#generic-variance",children:"container configuration option"}),"."]})})]})}function p(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(h,{...e})}):h(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>s});t(6540);var r=t(870);const a={tabItem:"tabItem_Ymn6"};var i=t(4848);function s(e){let{children:n,hidden:t,className:s}=e;return(0,i.jsx)("div",{role:"tabpanel",className:(0,r.A)(a.tabItem,s),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>b});var r=t(6540),a=t(870),i=t(3104),s=t(6347),l=t(205),o=t(7485),d=t(1682),c=t(9466);function v(e){return r.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,r.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function u(e){const{values:n,children:t}=e;return(0,r.useMemo)((()=>{const e=n??function(e){return v(e).map((e=>{let{props:{value:n,label:t,attributes:r,default:a}}=e;return{value:n,label:t,attributes:r,default:a}}))}(t);return function(e){const n=(0,d.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function h(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:t}=e;const a=(0,s.W6)(),i=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,o.aZ)(i),(0,r.useCallback)((e=>{if(!i)return;const n=new URLSearchParams(a.location.search);n.set(i,e),a.replace({...a.location,search:n.toString()})}),[i,a])]}function g(e){const{defaultValue:n,queryString:t=!1,groupId:a}=e,i=u(e),[s,o]=(0,r.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!h({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const r=t.find((e=>e.default))??t[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:n,tabValues:i}))),[d,v]=p({queryString:t,groupId:a}),[g,f]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[a,i]=(0,c.Dv)(t);return[a,(0,r.useCallback)((e=>{t&&i.set(e)}),[t,i])]}({groupId:a}),E=(()=>{const e=d??g;return h({value:e,tabValues:i})?e:null})();(0,l.A)((()=>{E&&o(E)}),[E]);return{selectedValue:s,selectValue:(0,r.useCallback)((e=>{if(!h({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);o(e),v(e),f(e)}),[v,f,i]),tabValues:i}}var f=t(2303);const E={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var m=t(4848);function x(e){let{className:n,block:t,selectedValue:r,selectValue:s,tabValues:l}=e;const o=[],{blockElementScrollPositionUntilNextRender:d}=(0,i.a_)(),c=e=>{const n=e.currentTarget,t=o.indexOf(n),a=l[t].value;a!==r&&(d(n),s(a))},v=e=>{let n=null;switch(e.key){case"Enter":c(e);break;case"ArrowRight":{const t=o.indexOf(e.currentTarget)+1;n=o[t]??o[0];break}case"ArrowLeft":{const t=o.indexOf(e.currentTarget)-1;n=o[t]??o[o.length-1];break}}n?.focus()};return(0,m.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,a.A)("tabs",{"tabs--block":t},n),children:l.map((e=>{let{value:n,label:t,attributes:i}=e;return(0,m.jsx)("li",{role:"tab",tabIndex:r===n?0:-1,"aria-selected":r===n,ref:e=>o.push(e),onKeyDown:v,onClick:c,...i,className:(0,a.A)("tabs__item",E.tabItem,i?.className,{"tabs__item--active":r===n}),children:t??n},n)}))})}function I(e){let{lazy:n,children:t,selectedValue:a}=e;const i=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===a));return e?(0,r.cloneElement)(e,{className:"margin-top--md"}):null}return(0,m.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,r.cloneElement)(e,{key:n,hidden:e.props.value!==a})))})}function j(e){const n=g(e);return(0,m.jsxs)("div",{className:(0,a.A)("tabs-container",E.tabList),children:[(0,m.jsx)(x,{...e,...n}),(0,m.jsx)(I,{...e,...n})]})}function b(e){const n=(0,f.A)();return(0,m.jsx)(j,{...e,children:v(e.children)},String(n))}},7470:(e,n,t)=>{t.d(n,{A:()=>s});var r=t(6540);const a={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var i=t(4848);function s(e){let{children:n}=e,t=r.Children.toArray(n).filter((e=>e));return(0,i.jsxs)("div",{className:a.codeDescContainer,children:[(0,i.jsx)("div",{className:a.desc,children:t[0]}),(0,i.jsx)("div",{className:a.example,children:t[1]})]})}},8453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>l});var r=t(6540);const a={},i=r.createContext(a);function s(e){const n=r.useContext(i);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:s(e.components),r.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[747],{5151:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>v,contentTitle:()=>d,default:()=>p,frontMatter:()=>o,metadata:()=>c,toc:()=>u});var r=t(4848),a=t(8453),i=t(7470),s=t(1470),l=t(9365);const o={},d="Generics",c={id:"advanced/generics",title:"Generics",description:"This section is about how Stashbox handles various usage scenarios that involve .NET Generic types. Including the registration of open-generic and closed-generic types, generic decorators, conditions based on generic constraints, and variance.",source:"@site/docs/advanced/generics.md",sourceDirName:"advanced",slug:"/advanced/generics",permalink:"/stashbox/docs/advanced/generics",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/advanced/generics.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Container configuration",permalink:"/stashbox/docs/configuration/container-configuration"},next:{title:"Decorators",permalink:"/stashbox/docs/advanced/decorators"}},v={},u=[{value:"Closed-generics",id:"closed-generics",level:2},{value:"Open-generics",id:"open-generics",level:2},{value:"Generic constraints",id:"generic-constraints",level:2},{value:"Variance",id:"variance",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,a.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h1,{id:"generics",children:"Generics"}),"\n",(0,r.jsxs)(n.p,{children:["This section is about how Stashbox handles various usage scenarios that involve .NET Generic types. Including the registration of open-generic and closed-generic types, ",(0,r.jsx)(n.a,{href:"/docs/advanced/decorators#generic-decorators",children:"generic decorators"}),", conditions based on generic constraints, and variance."]}),"\n",(0,r.jsx)(n.h2,{id:"closed-generics",children:"Closed-generics"}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsxs)("div",{children:[(0,r.jsx)(n.p,{children:"The registration of a closed-generic type does not differ from registering a simple non-generic service."}),(0,r.jsxs)(n.p,{children:["You have all options available that you saw at the ",(0,r.jsx)(n.a,{href:"/docs/guides/basics",children:"basic"})," and ",(0,r.jsx)(n.a,{href:"/docs/guides/advanced-registration",children:"advanced registration"})," flows."]})]}),(0,r.jsx)("div",{children:(0,r.jsxs)(s.A,{groupId:"generic-runtime-apis",children:[(0,r.jsx)(l.A,{value:"Generic API",label:"Generic API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"container.Register, UserValidator>();\nIValidator validator = container.Resolve>();\n"})})}),(0,r.jsx)(l.A,{value:"Runtime type API",label:"Runtime type API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"container.Register(typeof(IValidator), typeof(UserValidator));\nobject validator = container.Resolve(typeof(IValidator));\n"})})})]})})]}),"\n",(0,r.jsx)(n.h2,{id:"open-generics",children:"Open-generics"}),"\n",(0,r.jsxs)(n.p,{children:["The registration of an open-generic type differs from registering a closed-generic one as C# doesn't allow the usage of open-generic types in generic method parameters. We have to get a runtime type from the open-generic type first with ",(0,r.jsx)(n.code,{children:"typeof()"}),"."]}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsx)("div",{children:(0,r.jsx)(n.p,{children:"Open-generic types could help in such scenarios where you have generic interface-implementation pairs with numerous generic parameter variations. The registration of those different versions would look like this:"})}),(0,r.jsx)("div",{children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"container.Register, Validator>();\ncontainer.Register, Validator>();\ncontainer.Register, Validator>();\n// and so on...\n"})})})]}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsx)("div",{children:(0,r.jsxs)(n.p,{children:["Rather than doing that, you can register your type's generic definition and let Stashbox bind the type parameters for you. When a matching closed ",(0,r.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," is requested, the container will construct an equivalent closed-generic implementation."]})}),(0,r.jsx)("div",{children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"container.Register(typeof(IValidator<>), typeof(Validator<>));\n// Validator will be returned.\nIValidator userValidator = container.Resolve>();\n// Validator will be returned.\nIValidator roleValidator = container.Resolve>();\n"})})})]}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsx)("div",{children:(0,r.jsx)(n.p,{children:"A registered closed-generic type always has priority over an open-generic type at service selection."})}),(0,r.jsx)("div",{children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"container.Register, UserValidator>();\ncontainer.Register(typeof(IValidator<>), typeof(Validator<>));\n// UserValidator will be returned.\nIValidator validator = container.Resolve>();\n"})})})]}),"\n",(0,r.jsx)(n.h2,{id:"generic-constraints",children:"Generic constraints"}),"\n",(0,r.jsxs)(n.p,{children:["In the following examples, you can see how the container handles generic constraints during service resolution. Constraints can be used for ",(0,r.jsx)(n.a,{href:"/docs/guides/service-resolution#conditional-resolution",children:"conditional resolution"})," including collection filters."]}),"\n",(0,r.jsxs)(s.A,{children:[(0,r.jsxs)(l.A,{value:"Conditional resolution",label:"Conditional resolution",children:[(0,r.jsxs)(n.p,{children:["The container chooses ",(0,r.jsx)(n.code,{children:"UpdatedEventHandler"})," because it is the only one that has a constraint satisfied by the requested ",(0,r.jsx)(n.code,{children:"UserUpdatedEvent"})," generic parameter as it's implementing ",(0,r.jsx)(n.code,{children:"IUpdatedEvent"}),"."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"interface IEventHandler { }\n\n// event interfaces\ninterface IUpdatedEvent { }\ninterface ICreatedEvent { }\n\n// event handlers\nclass UpdatedEventHandler : IEventHandler where TEvent : IUpdatedEvent { }\nclass CreatedEventHandler : IEventHandler where TEvent : ICreatedEvent { }\n\n// event implementation\nclass UserUpdatedEvent : IUpdatedEvent { }\n\nusing var container = new StashboxContainer();\n\ncontainer.RegisterTypesAs(typeof(IEventHandler<>), new[] \n { \n typeof(UpdateEventHandler<>), \n typeof(CreateEventHandler<>) \n });\n\n// eventHandler will be UpdatedEventHandler\nIEventHandler eventHandler = container.Resolve>();\n"})})]}),(0,r.jsxs)(l.A,{value:"Collection filter",label:"Collection filter",children:[(0,r.jsx)(n.p,{children:"This example shows how the container is filtering out those services from the returned collection that does not satisfy the given generic constraint needed to create the closed generic type."}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"interface IEventHandler { }\n\n// event interfaces\ninterface IUpdatedEvent { }\ninterface ICreatedEvent { }\n\n// event handlers\nclass UpdatedEventHandler : IEventHandler where TEvent : IUpdatedEvent { }\nclass CreatedEventHandler : IEventHandler where TEvent : ICreatedEvent { }\n\n// event implementation\nclass UserUpdatedEvent : IUpdatedEvent { }\n\nusing var container = new StashboxContainer();\n\ncontainer.RegisterTypesAs(typeof(IEventHandler<>), new[] \n { \n typeof(UpdateEventHandler<>), \n typeof(CreateEventHandler<>) \n });\n\n// eventHandlers will contain only UpdatedEventHandler\nIEnumerable> eventHandlers = container.ResolveAll>();\n"})})]})]}),"\n",(0,r.jsx)(n.h2,{id:"variance",children:"Variance"}),"\n",(0,r.jsxs)(n.p,{children:["Since .NET Framework 4.0, C# supports ",(0,r.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/",children:"covariance and contravariance"})," in generic interfaces and delegates and allows implicit conversion of generic type parameters. In this section, we'll focus on variance in generic interfaces."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/creating-variant-generic-interfaces",children:"Here"})," you can read more about how to create variant generic interfaces, and the following example will show how you can use them with Stashbox."]}),"\n",(0,r.jsxs)(s.A,{children:[(0,r.jsxs)(l.A,{value:"Contravariance",label:"Contravariance",children:[(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Contravariance"})," only allows argument types that are less derived than that defined by the generic parameters. You can declare a generic type parameter contravariant by using the ",(0,r.jsx)(n.code,{children:"in"})," keyword."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"// contravariant generic event handler interface\ninterface IEventHandler { } \n\n// event interfaces\ninterface IGeneralEvent { }\ninterface IUpdatedEvent : IGeneralEvent { }\n\n// event handlers\nclass GeneralEventHandler : IEventHandler { }\nclass UpdatedEventHandler : IEventHandler { }\n\ncontainer.Register, GeneralEventHandler>();\ncontainer.Register, UpdatedEventHandler>();\n\n// eventHandlers contain both GeneralEventHandler and UpdatedEventHandler\nIEnumerable> eventHandlers = container.ResolveAll>();\n"})}),(0,r.jsxs)(n.p,{children:["Despite the fact that only ",(0,r.jsx)(n.code,{children:"IEventHandler"})," implementations were requested, the result contains both ",(0,r.jsx)(n.code,{children:"GeneralEventHandler"})," and ",(0,r.jsx)(n.code,{children:"UpdatedEventHandler"}),". As ",(0,r.jsx)(n.code,{children:"TEvent"})," is declared ",(0,r.jsx)(n.strong,{children:"contravariant"})," with the ",(0,r.jsx)(n.code,{children:"in"})," keyword, and ",(0,r.jsx)(n.code,{children:"IGeneralEvent"})," is less derived than ",(0,r.jsx)(n.code,{children:"IUpdatedEvent"}),", ",(0,r.jsx)(n.code,{children:"IEventHandler"})," implementations can be part of ",(0,r.jsx)(n.code,{children:"IEventHandler"})," collections."]}),(0,r.jsxs)(n.p,{children:["If we request ",(0,r.jsx)(n.code,{children:"IEventHandler"}),", only ",(0,r.jsx)(n.code,{children:"GeneralEventHandler"})," would be returned, because ",(0,r.jsx)(n.code,{children:"IUpdatedEvent"})," is more derived, so ",(0,r.jsx)(n.code,{children:"IEventHandler"})," implementations are not fit into ",(0,r.jsx)(n.code,{children:"IEventHandler"})," collections."]})]}),(0,r.jsxs)(l.A,{value:"Covariance",label:"Covariance",children:[(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Covariance"})," only allows argument types that are more derived than that defined by the generic parameters. You can declare a generic type parameter covariant by using the ",(0,r.jsx)(n.code,{children:"out"})," keyword."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"// covariant generic event handler interface\ninterface IEventHandler { } \n\n// event interfaces\ninterface IGeneralEvent { }\ninterface IUpdatedEvent : IGeneralEvent { }\n\n// event handlers\nclass GeneralEventHandler : IEventHandler { }\nclass UpdatedEventHandler : IEventHandler { }\n\ncontainer.Register, GeneralEventHandler>();\ncontainer.Register, UpdatedEventHandler>();\n\n// eventHandlers contain both GeneralEventHandler and UpdatedEventHandler\nIEnumerable> eventHandlers = container.ResolveAll>();\n"})}),(0,r.jsxs)(n.p,{children:["Despite the fact that only ",(0,r.jsx)(n.code,{children:"IEventHandler"})," implementations were requested, the result contains both ",(0,r.jsx)(n.code,{children:"GeneralEventHandler"})," and ",(0,r.jsx)(n.code,{children:"UpdatedEventHandler"}),". As ",(0,r.jsx)(n.code,{children:"TEvent"})," is declared ",(0,r.jsx)(n.strong,{children:"covariant"})," with the ",(0,r.jsx)(n.code,{children:"out"})," keyword, and ",(0,r.jsx)(n.code,{children:"IUpdatedEvent"})," is more derived than ",(0,r.jsx)(n.code,{children:"IGeneralEvent"}),", ",(0,r.jsx)(n.code,{children:"IEventHandler"})," implementations can be part of ",(0,r.jsx)(n.code,{children:"IEventHandler"})," collections."]}),(0,r.jsxs)(n.p,{children:["If we request ",(0,r.jsx)(n.code,{children:"IEventHandler"}),", only ",(0,r.jsx)(n.code,{children:"UpdatedEventHandler"})," would be returned, because ",(0,r.jsx)(n.code,{children:"IGeneralEvent"})," is less derived, so ",(0,r.jsx)(n.code,{children:"IEventHandler"})," implementations are not fit into ",(0,r.jsx)(n.code,{children:"IEventHandler"})," collections."]})]})]}),"\n",(0,r.jsx)(n.admonition,{type:"info",children:(0,r.jsxs)(n.p,{children:["The check for variant generic types is enabled by default, but it can be turned off via a ",(0,r.jsx)(n.a,{href:"/docs/configuration/container-configuration#generic-variance",children:"container configuration option"}),"."]})})]})}function p(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(h,{...e})}):h(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>s});t(6540);var r=t(870);const a={tabItem:"tabItem_Ymn6"};var i=t(4848);function s(e){let{children:n,hidden:t,className:s}=e;return(0,i.jsx)("div",{role:"tabpanel",className:(0,r.A)(a.tabItem,s),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>b});var r=t(6540),a=t(870),i=t(3104),s=t(6347),l=t(205),o=t(7485),d=t(1682),c=t(9466);function v(e){return r.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,r.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function u(e){const{values:n,children:t}=e;return(0,r.useMemo)((()=>{const e=n??function(e){return v(e).map((e=>{let{props:{value:n,label:t,attributes:r,default:a}}=e;return{value:n,label:t,attributes:r,default:a}}))}(t);return function(e){const n=(0,d.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function h(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:t}=e;const a=(0,s.W6)(),i=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,o.aZ)(i),(0,r.useCallback)((e=>{if(!i)return;const n=new URLSearchParams(a.location.search);n.set(i,e),a.replace({...a.location,search:n.toString()})}),[i,a])]}function g(e){const{defaultValue:n,queryString:t=!1,groupId:a}=e,i=u(e),[s,o]=(0,r.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!h({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const r=t.find((e=>e.default))??t[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:n,tabValues:i}))),[d,v]=p({queryString:t,groupId:a}),[g,f]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[a,i]=(0,c.Dv)(t);return[a,(0,r.useCallback)((e=>{t&&i.set(e)}),[t,i])]}({groupId:a}),E=(()=>{const e=d??g;return h({value:e,tabValues:i})?e:null})();(0,l.A)((()=>{E&&o(E)}),[E]);return{selectedValue:s,selectValue:(0,r.useCallback)((e=>{if(!h({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);o(e),v(e),f(e)}),[v,f,i]),tabValues:i}}var f=t(2303);const E={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var m=t(4848);function x(e){let{className:n,block:t,selectedValue:r,selectValue:s,tabValues:l}=e;const o=[],{blockElementScrollPositionUntilNextRender:d}=(0,i.a_)(),c=e=>{const n=e.currentTarget,t=o.indexOf(n),a=l[t].value;a!==r&&(d(n),s(a))},v=e=>{let n=null;switch(e.key){case"Enter":c(e);break;case"ArrowRight":{const t=o.indexOf(e.currentTarget)+1;n=o[t]??o[0];break}case"ArrowLeft":{const t=o.indexOf(e.currentTarget)-1;n=o[t]??o[o.length-1];break}}n?.focus()};return(0,m.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,a.A)("tabs",{"tabs--block":t},n),children:l.map((e=>{let{value:n,label:t,attributes:i}=e;return(0,m.jsx)("li",{role:"tab",tabIndex:r===n?0:-1,"aria-selected":r===n,ref:e=>o.push(e),onKeyDown:v,onClick:c,...i,className:(0,a.A)("tabs__item",E.tabItem,i?.className,{"tabs__item--active":r===n}),children:t??n},n)}))})}function I(e){let{lazy:n,children:t,selectedValue:a}=e;const i=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===a));return e?(0,r.cloneElement)(e,{className:"margin-top--md"}):null}return(0,m.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,r.cloneElement)(e,{key:n,hidden:e.props.value!==a})))})}function j(e){const n=g(e);return(0,m.jsxs)("div",{className:(0,a.A)("tabs-container",E.tabList),children:[(0,m.jsx)(x,{...e,...n}),(0,m.jsx)(I,{...e,...n})]})}function b(e){const n=(0,f.A)();return(0,m.jsx)(j,{...e,children:v(e.children)},String(n))}},7470:(e,n,t)=>{t.d(n,{A:()=>s});var r=t(6540);const a={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var i=t(4848);function s(e){let{children:n}=e,t=r.Children.toArray(n).filter((e=>e));return(0,i.jsxs)("div",{className:a.codeDescContainer,children:[(0,i.jsx)("div",{className:a.desc,children:t[0]}),(0,i.jsx)("div",{className:a.example,children:t[1]})]})}},8453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>l});var r=t(6540);const a={},i=r.createContext(a);function s(e){const n=r.useContext(i);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:s(e.components),r.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/74fdf922.0b9b5797.js b/assets/js/74fdf922.71931827.js similarity index 99% rename from assets/js/74fdf922.0b9b5797.js rename to assets/js/74fdf922.71931827.js index 27d86513..0c5976b0 100644 --- a/assets/js/74fdf922.0b9b5797.js +++ b/assets/js/74fdf922.71931827.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[799],{4149:(e,s,n)=>{n.r(s),n.d(s,{assets:()=>p,contentTitle:()=>l,default:()=>b,frontMatter:()=>r,metadata:()=>d,toc:()=>u});var i=n(4848),o=n(8453),a=n(7470),t=n(1470),c=n(9365);const r={},l="Scopes",d={id:"guides/scopes",title:"Scopes",description:"A scope is Stashbox's implementation of the unit-of-work pattern; it encapsulates a given unit used to resolve and store instances required for a given work. When a scoped service is resolved or injected, the scope ensures that it gets instantiated only once within the scope's lifetime. When the work is finished, the scope cleans up the resources by disposing each tracked disposable instance.",source:"@site/docs/guides/scopes.md",sourceDirName:"guides",slug:"/guides/scopes",permalink:"/stashbox/docs/guides/scopes",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/guides/scopes.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Lifetimes",permalink:"/stashbox/docs/guides/lifetimes"},next:{title:"Registration configuration",permalink:"/stashbox/docs/configuration/registration-configuration"}},p={},u=[{value:"Creating a scope",id:"creating-a-scope",level:2},{value:"Named scopes",id:"named-scopes",level:2},{value:"Service as scope",id:"service-as-scope",level:2},{value:"Put instance to a scope",id:"put-instance-to-a-scope",level:2},{value:"Disposal",id:"disposal",level:2},{value:"Async disposal",id:"async-disposal",level:3},{value:"Finalizer delegate",id:"finalizer-delegate",level:3}];function h(e){const s={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",p:"p",pre:"pre",strong:"strong",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.h1,{id:"scopes",children:"Scopes"}),"\n",(0,i.jsx)(s.p,{children:"A scope is Stashbox's implementation of the unit-of-work pattern; it encapsulates a given unit used to resolve and store instances required for a given work. When a scoped service is resolved or injected, the scope ensures that it gets instantiated only once within the scope's lifetime. When the work is finished, the scope cleans up the resources by disposing each tracked disposable instance."}),"\n",(0,i.jsx)(s.p,{children:"A web application is a fair usage example for scopes as it has a well-defined execution unit that can be bound to a scope - the HTTP request. Every request could have its unique scope attached to the request's lifetime. When a request ends, the scope gets closed, and all the scoped instances will be disposed."}),"\n",(0,i.jsx)(s.h2,{id:"creating-a-scope",children:"Creating a scope"}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(s.p,{children:["You can create a scope from the container by calling its ",(0,i.jsx)(s.code,{children:".BeginScope()"})," method."]}),(0,i.jsxs)(s.p,{children:["Scopes can be ",(0,i.jsx)(s.strong,{children:"nested"}),", which means you can create sub-scopes from existing ones with their ",(0,i.jsx)(s.code,{children:".BeginScope()"})," method."]}),(0,i.jsx)(s.p,{children:"Scoped service instances are not shared across parent and sub-scope relations."}),(0,i.jsxs)(s.p,{children:["Nested scopes can be ",(0,i.jsx)(s.strong,{children:"attached to their parent's lifetime"}),", which means when a parent gets disposed all child scopes attached to it will be disposed."]}),(0,i.jsxs)(s.p,{children:["Scopes are ",(0,i.jsx)(s.code,{children:"IDisposable"}),"; they track all ",(0,i.jsx)(s.code,{children:"IDisposable"})," instances they resolved. Calling their ",(0,i.jsx)(s.code,{children:"Dispose()"})," method or wrapping them in ",(0,i.jsx)(s.code,{children:"using"})," statements is a crucial part of their service's lifetime management."]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(t.A,{children:[(0,i.jsx)(c.A,{value:"Create",label:"Create",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"container.RegisterScoped();\n\n// create the scope with using so it'll be auto disposed.\nusing (var scope = container.BeginScope())\n{\n IJob job = scope.Resolve();\n IJob jobAgain = scope.Resolve();\n // job and jobAgain are created in the \n // same scope, so they are the same instance.\n}\n"})})}),(0,i.jsx)(c.A,{value:"Nested",label:"Nested",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"container.RegisterScoped();\n\nusing (var parent = container.BeginScope())\n{\n IJob job = parent.Resolve();\n IJob jobAgain = parent.Resolve();\n // job and jobAgain are created in the \n // same scope, so they are the same instance.\n\n // create a sub-scope.\n using var sub = parent.BeginScope();\n\n IJob subJob = sub.Resolve();\n // subJob is a new instance created in the sub-scope, \n // differs from either job and jobAgain.\n}\n"})})}),(0,i.jsx)(c.A,{value:"Nested attached",label:"Nested attached",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"container.RegisterScoped();\n\nvar parent = container.BeginScope();\nvar sub = parent.BeginScope(attachToParent: true);\n\n// sub will also be disposed with the scope.\nscope.Dispose(); \n"})})})]})})]}),"\n",(0,i.jsx)(s.h2,{id:"named-scopes",children:"Named scopes"}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(s.p,{children:"There might be cases where you don't want to use a service globally across every scope, only in specific ones."}),(0,i.jsxs)(s.p,{children:["For this reason, you can differentiate specific scope groups from other scopes with a ",(0,i.jsx)(s.strong,{children:"name"}),"."]}),(0,i.jsxs)(s.p,{children:["You can set a service's lifetime to ",(0,i.jsx)(s.a,{href:"/docs/guides/lifetimes#named-scope-lifetime",children:"named scope lifetime"})," initialized with the ",(0,i.jsx)(s.strong,{children:"scope's name"})," to mark it usable only for that named scope."]}),(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:'container.Register(options => \n options.InNamedScope("DbScope"));\n\ncontainer.Register(options => \n options.InNamedScope("DbScope"));\n\ncontainer.Register(options => \n options.InNamedScope("DbSubScope"));\n\ncontainer.Register(options => \n options.InNamedScope("StorageScope"));\n'})}),(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsxs)(s.p,{children:["Services with named scope lifetime are ",(0,i.jsx)(s.strong,{children:"shared across parent and sub-scope relations"}),"."]})}),(0,i.jsx)(s.p,{children:"If you request a name-scoped service from an un-named scope, you'll get an error or no result (depending on the configuration) because those services are selectable only by named scopes with a matching name."})]}),(0,i.jsx)("div",{children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:'using (var dbScope = container.BeginScope("DbScope"))\n{ \n // DbBackup and DbCleanup will be returned.\n IEnumerable jobs = dbScope.ResolveAll();\n\n // create a sub-scope of dbScope.\n using var sub = dbScope.BeginScope();\n\n // DbBackup and DbCleanup will be returned from the named parent scope.\n IEnumerable jobs = sub.ResolveAll();\n\n // create a named sub-scope.\n using var namedSub = dbScope.BeginScope("DbSubScope");\n // DbIndexRebuild will be returned from the named sub-scope.\n IEnumerable jobs = namedSub.ResolveAll();\n}\n\nusing (var storageScope = container.BeginScope("StorageScope"))\n{\n // StorageCleanup will be returned.\n IJob job = storageScope.Resolve();\n}\n\n// create a common scope without a name.\nusing (var unNamed = container.BeginScope())\n{\n // empty result as there\'s no service registered without named scope.\n IEnumerable jobs = unNamed.ResolveAll();\n\n // throws an exception because there\'s no unnamed service registered.\n IJob job = unNamed.Resolve();\n}\n'})})})]}),"\n",(0,i.jsx)(s.h2,{id:"service-as-scope",children:"Service as scope"}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(s.p,{children:"You can configure a service to behave like a nested named scope. At the resolution of this kind of service, a new dedicated named scope is created implicitly for managing the service's dependencies."}),(0,i.jsx)(s.p,{children:"With this feature, you can organize your dependencies around logical groups (named scopes) instead of individual services."}),(0,i.jsxs)(s.p,{children:["Using ",(0,i.jsx)(s.code,{children:"InScopeDefinedBy()"}),", you can bind services to a defined scope without giving it a name. In this case, the defining service's ",(0,i.jsx)(s.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," is used for naming the scope."]}),(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsx)(s.p,{children:"The lifetime of the defined scope is attached to the current scope that was used to create the service."})})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(t.A,{children:[(0,i.jsx)(c.A,{value:"Define named",label:"Define named",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:'container.Register(options => options\n .DefinesScope("DbBackupScope"));\ncontainer.Register(options => options\n .InNamedScope("DbBackupScope"));\ncontainer.Register();\n\nvar scope = container.BeginScope();\n\n// DbBackup will create a named scope with the name "DbBackupScope".\n// the named scope will select ConsoleLogger as it\'s \n// bound to the named scope\'s identifier.\nIJob job = scope.Resolve();\n\n// this will dispose the implicitly created named scope by DbBackup.\nscope.Dispose(); \n'})})}),(0,i.jsx)(c.A,{value:"Define typed",label:"Define typed",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"container.Register(options => options\n .DefinesScope());\ncontainer.Register(options => options\n .InScopeDefinedBy());\ncontainer.Register();\n\nvar scope = container.BeginScope();\n\n// DbBackup will create a named scope with the name typeof(DbBackup).\n// the named scope will select ConsoleLogger as it's \n// bound to the named scope's identifier.\nIJob job = scope.Resolve();\n\n// this will dispose the implicitly created named scope by DbBackup.\nscope.Dispose(); \n"})})})]})})]}),"\n",(0,i.jsx)(s.h2,{id:"put-instance-to-a-scope",children:"Put instance to a scope"}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsx)("div",{children:(0,i.jsx)(s.p,{children:"You can add an already instantiated service to a scope. The instance's lifetime will be tracked by the given scope."})}),(0,i.jsx)("div",{children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"using var scope = container.BeginScope();\nscope.PutInstanceInScope(new DbBackup());\n"})})})]}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsx)("div",{children:(0,i.jsxs)(s.p,{children:["You can disable the tracking by passing ",(0,i.jsx)(s.code,{children:"true"})," for the ",(0,i.jsx)(s.code,{children:"withoutDisposalTracking"})," parameter. In this case, only the strong reference to the instance is dropped when the scope is disposed."]})}),(0,i.jsx)("div",{children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"using var scope = container.BeginScope();\nscope.PutInstanceInScope(new DbBackup(), withoutDisposalTracking: true);\n"})})})]}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsx)("div",{children:(0,i.jsxs)(s.p,{children:["You can also give your instance a name to use it like a ",(0,i.jsx)(s.a,{href:"/docs/guides/basics#named-registration",children:"named registration"}),":"]})}),(0,i.jsx)("div",{children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:'using var scope = container.BeginScope();\nscope.PutInstanceInScope(new DbBackup(), false, name: "DbBackup");\n'})})})]}),"\n",(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsxs)(s.p,{children:["Instances put to a scope will take precedence over existing registrations with the same ",(0,i.jsx)(s.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),"."]})}),"\n",(0,i.jsx)(s.h2,{id:"disposal",children:"Disposal"}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(s.p,{children:["The currently resolving scope tracks services that implement either ",(0,i.jsx)(s.code,{children:"IDisposable"})," or ",(0,i.jsx)(s.code,{children:"IAsyncDisposable"}),". This means that when the scope is disposed, all the tracked disposable instances will be disposed with it."]}),(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsx)(s.p,{children:"Disposing the container will dispose all the singleton instances and their dependencies."})})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(t.A,{groupId:"lifetime-dispose",children:[(0,i.jsx)(c.A,{value:"Using",label:"Using",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"using (var scope = container.BeginScope())\n{\n var disposable = scope.Resolve();\n} // 'disposable' will be disposed when \n // the using statement ends.\n"})})}),(0,i.jsx)(c.A,{value:"Dispose",label:"Dispose",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"var scope = container.BeginScope();\nvar disposable = scope.Resolve();\n\n// 'disposable' will be disposed with the scope.\nscope.Dispose();\n"})})})]})})]}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsx)("div",{children:(0,i.jsxs)(s.p,{children:["You can disable the disposal tracking on a ",(0,i.jsx)(s.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"service registration"})," with the ",(0,i.jsx)(s.code,{children:".WithoutDisposalTracking()"})," option."]})}),(0,i.jsx)("div",{children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"container.Register(options => \n options.WithoutDisposalTracking());\n"})})})]}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(s.h3,{id:"async-disposal",children:"Async disposal"}),(0,i.jsxs)(s.p,{children:["As the container and its scopes implement the ",(0,i.jsx)(s.code,{children:"IAsyncDisposable"})," interface, you can dispose them asynchronously when they are used in an ",(0,i.jsx)(s.code,{children:"async"})," context."]}),(0,i.jsxs)(s.p,{children:["Calling ",(0,i.jsx)(s.code,{children:"DisposeAsync"})," disposes both ",(0,i.jsx)(s.code,{children:"IDisposable"})," and ",(0,i.jsx)(s.code,{children:"IAsyncDisposable"})," instances; however, calling ",(0,i.jsx)(s.code,{children:"Dispose"})," only disposes ",(0,i.jsx)(s.code,{children:"IDisposable"})," instances."]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(t.A,{groupId:"lifetime-dispose",children:[(0,i.jsx)(c.A,{value:"Using",label:"Using",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"await using (var scope = container.BeginScope())\n{\n var disposable = scope.Resolve();\n} // 'disposable' will be disposed asynchronously \n // when the using statement ends.\n"})})}),(0,i.jsx)(c.A,{value:"Dispose",label:"Dispose",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"var scope = container.BeginScope();\nvar disposable = scope.Resolve();\n\n// 'disposable' will be disposed asynchronously with the scope.\nawait scope.DisposeAsync();\n"})})})]})})]}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(s.h3,{id:"finalizer-delegate",children:"Finalizer delegate"}),(0,i.jsxs)(s.p,{children:["During ",(0,i.jsx)(s.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"service registration"}),", you can set a custom finalizer delegate that will be invoked at the service's disposal."]})]}),(0,i.jsx)("div",{children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"container.Register(options => \n options.WithFinalizer(backup => \n backup.CloseDbConnection()));\n"})})})]})]})}function b(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},9365:(e,s,n)=>{n.d(s,{A:()=>t});n(6540);var i=n(870);const o={tabItem:"tabItem_Ymn6"};var a=n(4848);function t(e){let{children:s,hidden:n,className:t}=e;return(0,a.jsx)("div",{role:"tabpanel",className:(0,i.A)(o.tabItem,t),hidden:n,children:s})}},1470:(e,s,n)=>{n.d(s,{A:()=>w});var i=n(6540),o=n(870),a=n(3104),t=n(6347),c=n(205),r=n(7485),l=n(1682),d=n(9466);function p(e){return i.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,i.isValidElement)(e)&&function(e){const{props:s}=e;return!!s&&"object"==typeof s&&"value"in s}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function u(e){const{values:s,children:n}=e;return(0,i.useMemo)((()=>{const e=s??function(e){return p(e).map((e=>{let{props:{value:s,label:n,attributes:i,default:o}}=e;return{value:s,label:n,attributes:i,default:o}}))}(n);return function(e){const s=(0,l.X)(e,((e,s)=>e.value===s.value));if(s.length>0)throw new Error(`Docusaurus error: Duplicate values "${s.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[s,n])}function h(e){let{value:s,tabValues:n}=e;return n.some((e=>e.value===s))}function b(e){let{queryString:s=!1,groupId:n}=e;const o=(0,t.W6)(),a=function(e){let{queryString:s=!1,groupId:n}=e;if("string"==typeof s)return s;if(!1===s)return null;if(!0===s&&!n)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:s,groupId:n});return[(0,r.aZ)(a),(0,i.useCallback)((e=>{if(!a)return;const s=new URLSearchParams(o.location.search);s.set(a,e),o.replace({...o.location,search:s.toString()})}),[a,o])]}function g(e){const{defaultValue:s,queryString:n=!1,groupId:o}=e,a=u(e),[t,r]=(0,i.useState)((()=>function(e){let{defaultValue:s,tabValues:n}=e;if(0===n.length)throw new Error("Docusaurus error: the component requires at least one children component");if(s){if(!h({value:s,tabValues:n}))throw new Error(`Docusaurus error: The has a defaultValue "${s}" but none of its children has the corresponding value. Available values are: ${n.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return s}const i=n.find((e=>e.default))??n[0];if(!i)throw new Error("Unexpected error: 0 tabValues");return i.value}({defaultValue:s,tabValues:a}))),[l,p]=b({queryString:n,groupId:o}),[g,m]=function(e){let{groupId:s}=e;const n=function(e){return e?`docusaurus.tab.${e}`:null}(s),[o,a]=(0,d.Dv)(n);return[o,(0,i.useCallback)((e=>{n&&a.set(e)}),[n,a])]}({groupId:o}),v=(()=>{const e=l??g;return h({value:e,tabValues:a})?e:null})();(0,c.A)((()=>{v&&r(v)}),[v]);return{selectedValue:t,selectValue:(0,i.useCallback)((e=>{if(!h({value:e,tabValues:a}))throw new Error(`Can't select invalid tab value=${e}`);r(e),p(e),m(e)}),[p,m,a]),tabValues:a}}var m=n(2303);const v={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var x=n(4848);function j(e){let{className:s,block:n,selectedValue:i,selectValue:t,tabValues:c}=e;const r=[],{blockElementScrollPositionUntilNextRender:l}=(0,a.a_)(),d=e=>{const s=e.currentTarget,n=r.indexOf(s),o=c[n].value;o!==i&&(l(s),t(o))},p=e=>{let s=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const n=r.indexOf(e.currentTarget)+1;s=r[n]??r[0];break}case"ArrowLeft":{const n=r.indexOf(e.currentTarget)-1;s=r[n]??r[r.length-1];break}}s?.focus()};return(0,x.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,o.A)("tabs",{"tabs--block":n},s),children:c.map((e=>{let{value:s,label:n,attributes:a}=e;return(0,x.jsx)("li",{role:"tab",tabIndex:i===s?0:-1,"aria-selected":i===s,ref:e=>r.push(e),onKeyDown:p,onClick:d,...a,className:(0,o.A)("tabs__item",v.tabItem,a?.className,{"tabs__item--active":i===s}),children:n??s},s)}))})}function f(e){let{lazy:s,children:n,selectedValue:o}=e;const a=(Array.isArray(n)?n:[n]).filter(Boolean);if(s){const e=a.find((e=>e.props.value===o));return e?(0,i.cloneElement)(e,{className:"margin-top--md"}):null}return(0,x.jsx)("div",{className:"margin-top--md",children:a.map(((e,s)=>(0,i.cloneElement)(e,{key:s,hidden:e.props.value!==o})))})}function y(e){const s=g(e);return(0,x.jsxs)("div",{className:(0,o.A)("tabs-container",v.tabList),children:[(0,x.jsx)(j,{...e,...s}),(0,x.jsx)(f,{...e,...s})]})}function w(e){const s=(0,m.A)();return(0,x.jsx)(y,{...e,children:p(e.children)},String(s))}},7470:(e,s,n)=>{n.d(s,{A:()=>t});var i=n(6540);const o={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var a=n(4848);function t(e){let{children:s}=e,n=i.Children.toArray(s).filter((e=>e));return(0,a.jsxs)("div",{className:o.codeDescContainer,children:[(0,a.jsx)("div",{className:o.desc,children:n[0]}),(0,a.jsx)("div",{className:o.example,children:n[1]})]})}},8453:(e,s,n)=>{n.d(s,{R:()=>t,x:()=>c});var i=n(6540);const o={},a=i.createContext(o);function t(e){const s=i.useContext(a);return i.useMemo((function(){return"function"==typeof e?e(s):{...s,...e}}),[s,e])}function c(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:t(e.components),i.createElement(a.Provider,{value:s},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[799],{4149:(e,s,n)=>{n.r(s),n.d(s,{assets:()=>p,contentTitle:()=>l,default:()=>b,frontMatter:()=>r,metadata:()=>d,toc:()=>u});var i=n(4848),o=n(8453),a=n(7470),t=n(1470),c=n(9365);const r={},l="Scopes",d={id:"guides/scopes",title:"Scopes",description:"A scope is Stashbox's implementation of the unit-of-work pattern; it encapsulates a given unit used to resolve and store instances required for a given work. When a scoped service is resolved or injected, the scope ensures that it gets instantiated only once within the scope's lifetime. When the work is finished, the scope cleans up the resources by disposing each tracked disposable instance.",source:"@site/docs/guides/scopes.md",sourceDirName:"guides",slug:"/guides/scopes",permalink:"/stashbox/docs/guides/scopes",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/guides/scopes.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Lifetimes",permalink:"/stashbox/docs/guides/lifetimes"},next:{title:"Registration configuration",permalink:"/stashbox/docs/configuration/registration-configuration"}},p={},u=[{value:"Creating a scope",id:"creating-a-scope",level:2},{value:"Named scopes",id:"named-scopes",level:2},{value:"Service as scope",id:"service-as-scope",level:2},{value:"Put instance to a scope",id:"put-instance-to-a-scope",level:2},{value:"Disposal",id:"disposal",level:2},{value:"Async disposal",id:"async-disposal",level:3},{value:"Finalizer delegate",id:"finalizer-delegate",level:3}];function h(e){const s={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",p:"p",pre:"pre",strong:"strong",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.h1,{id:"scopes",children:"Scopes"}),"\n",(0,i.jsx)(s.p,{children:"A scope is Stashbox's implementation of the unit-of-work pattern; it encapsulates a given unit used to resolve and store instances required for a given work. When a scoped service is resolved or injected, the scope ensures that it gets instantiated only once within the scope's lifetime. When the work is finished, the scope cleans up the resources by disposing each tracked disposable instance."}),"\n",(0,i.jsx)(s.p,{children:"A web application is a fair usage example for scopes as it has a well-defined execution unit that can be bound to a scope - the HTTP request. Every request could have its unique scope attached to the request's lifetime. When a request ends, the scope gets closed, and all the scoped instances will be disposed."}),"\n",(0,i.jsx)(s.h2,{id:"creating-a-scope",children:"Creating a scope"}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(s.p,{children:["You can create a scope from the container by calling its ",(0,i.jsx)(s.code,{children:".BeginScope()"})," method."]}),(0,i.jsxs)(s.p,{children:["Scopes can be ",(0,i.jsx)(s.strong,{children:"nested"}),", which means you can create sub-scopes from existing ones with their ",(0,i.jsx)(s.code,{children:".BeginScope()"})," method."]}),(0,i.jsx)(s.p,{children:"Scoped service instances are not shared across parent and sub-scope relations."}),(0,i.jsxs)(s.p,{children:["Nested scopes can be ",(0,i.jsx)(s.strong,{children:"attached to their parent's lifetime"}),", which means when a parent gets disposed all child scopes attached to it will be disposed."]}),(0,i.jsxs)(s.p,{children:["Scopes are ",(0,i.jsx)(s.code,{children:"IDisposable"}),"; they track all ",(0,i.jsx)(s.code,{children:"IDisposable"})," instances they resolved. Calling their ",(0,i.jsx)(s.code,{children:"Dispose()"})," method or wrapping them in ",(0,i.jsx)(s.code,{children:"using"})," statements is a crucial part of their service's lifetime management."]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(t.A,{children:[(0,i.jsx)(c.A,{value:"Create",label:"Create",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"container.RegisterScoped();\n\n// create the scope with using so it'll be auto disposed.\nusing (var scope = container.BeginScope())\n{\n IJob job = scope.Resolve();\n IJob jobAgain = scope.Resolve();\n // job and jobAgain are created in the \n // same scope, so they are the same instance.\n}\n"})})}),(0,i.jsx)(c.A,{value:"Nested",label:"Nested",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"container.RegisterScoped();\n\nusing (var parent = container.BeginScope())\n{\n IJob job = parent.Resolve();\n IJob jobAgain = parent.Resolve();\n // job and jobAgain are created in the \n // same scope, so they are the same instance.\n\n // create a sub-scope.\n using var sub = parent.BeginScope();\n\n IJob subJob = sub.Resolve();\n // subJob is a new instance created in the sub-scope, \n // differs from either job and jobAgain.\n}\n"})})}),(0,i.jsx)(c.A,{value:"Nested attached",label:"Nested attached",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"container.RegisterScoped();\n\nvar parent = container.BeginScope();\nvar sub = parent.BeginScope(attachToParent: true);\n\n// sub will also be disposed with the scope.\nscope.Dispose(); \n"})})})]})})]}),"\n",(0,i.jsx)(s.h2,{id:"named-scopes",children:"Named scopes"}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(s.p,{children:"There might be cases where you don't want to use a service globally across every scope, only in specific ones."}),(0,i.jsxs)(s.p,{children:["For this reason, you can differentiate specific scope groups from other scopes with a ",(0,i.jsx)(s.strong,{children:"name"}),"."]}),(0,i.jsxs)(s.p,{children:["You can set a service's lifetime to ",(0,i.jsx)(s.a,{href:"/docs/guides/lifetimes#named-scope-lifetime",children:"named scope lifetime"})," initialized with the ",(0,i.jsx)(s.strong,{children:"scope's name"})," to mark it usable only for that named scope."]}),(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:'container.Register(options => \n options.InNamedScope("DbScope"));\n\ncontainer.Register(options => \n options.InNamedScope("DbScope"));\n\ncontainer.Register(options => \n options.InNamedScope("DbSubScope"));\n\ncontainer.Register(options => \n options.InNamedScope("StorageScope"));\n'})}),(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsxs)(s.p,{children:["Services with named scope lifetime are ",(0,i.jsx)(s.strong,{children:"shared across parent and sub-scope relations"}),"."]})}),(0,i.jsx)(s.p,{children:"If you request a name-scoped service from an un-named scope, you'll get an error or no result (depending on the configuration) because those services are selectable only by named scopes with a matching name."})]}),(0,i.jsx)("div",{children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:'using (var dbScope = container.BeginScope("DbScope"))\n{ \n // DbBackup and DbCleanup will be returned.\n IEnumerable jobs = dbScope.ResolveAll();\n\n // create a sub-scope of dbScope.\n using var sub = dbScope.BeginScope();\n\n // DbBackup and DbCleanup will be returned from the named parent scope.\n IEnumerable jobs = sub.ResolveAll();\n\n // create a named sub-scope.\n using var namedSub = dbScope.BeginScope("DbSubScope");\n // DbIndexRebuild will be returned from the named sub-scope.\n IEnumerable jobs = namedSub.ResolveAll();\n}\n\nusing (var storageScope = container.BeginScope("StorageScope"))\n{\n // StorageCleanup will be returned.\n IJob job = storageScope.Resolve();\n}\n\n// create a common scope without a name.\nusing (var unNamed = container.BeginScope())\n{\n // empty result as there\'s no service registered without named scope.\n IEnumerable jobs = unNamed.ResolveAll();\n\n // throws an exception because there\'s no unnamed service registered.\n IJob job = unNamed.Resolve();\n}\n'})})})]}),"\n",(0,i.jsx)(s.h2,{id:"service-as-scope",children:"Service as scope"}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(s.p,{children:"You can configure a service to behave like a nested named scope. At the resolution of this kind of service, a new dedicated named scope is created implicitly for managing the service's dependencies."}),(0,i.jsx)(s.p,{children:"With this feature, you can organize your dependencies around logical groups (named scopes) instead of individual services."}),(0,i.jsxs)(s.p,{children:["Using ",(0,i.jsx)(s.code,{children:"InScopeDefinedBy()"}),", you can bind services to a defined scope without giving it a name. In this case, the defining service's ",(0,i.jsx)(s.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," is used for naming the scope."]}),(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsx)(s.p,{children:"The lifetime of the defined scope is attached to the current scope that was used to create the service."})})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(t.A,{children:[(0,i.jsx)(c.A,{value:"Define named",label:"Define named",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:'container.Register(options => options\n .DefinesScope("DbBackupScope"));\ncontainer.Register(options => options\n .InNamedScope("DbBackupScope"));\ncontainer.Register();\n\nvar scope = container.BeginScope();\n\n// DbBackup will create a named scope with the name "DbBackupScope".\n// the named scope will select ConsoleLogger as it\'s \n// bound to the named scope\'s identifier.\nIJob job = scope.Resolve();\n\n// this will dispose the implicitly created named scope by DbBackup.\nscope.Dispose(); \n'})})}),(0,i.jsx)(c.A,{value:"Define typed",label:"Define typed",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"container.Register(options => options\n .DefinesScope());\ncontainer.Register(options => options\n .InScopeDefinedBy());\ncontainer.Register();\n\nvar scope = container.BeginScope();\n\n// DbBackup will create a named scope with the name typeof(DbBackup).\n// the named scope will select ConsoleLogger as it's \n// bound to the named scope's identifier.\nIJob job = scope.Resolve();\n\n// this will dispose the implicitly created named scope by DbBackup.\nscope.Dispose(); \n"})})})]})})]}),"\n",(0,i.jsx)(s.h2,{id:"put-instance-to-a-scope",children:"Put instance to a scope"}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsx)("div",{children:(0,i.jsx)(s.p,{children:"You can add an already instantiated service to a scope. The instance's lifetime will be tracked by the given scope."})}),(0,i.jsx)("div",{children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"using var scope = container.BeginScope();\nscope.PutInstanceInScope(new DbBackup());\n"})})})]}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsx)("div",{children:(0,i.jsxs)(s.p,{children:["You can disable the tracking by passing ",(0,i.jsx)(s.code,{children:"true"})," for the ",(0,i.jsx)(s.code,{children:"withoutDisposalTracking"})," parameter. In this case, only the strong reference to the instance is dropped when the scope is disposed."]})}),(0,i.jsx)("div",{children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"using var scope = container.BeginScope();\nscope.PutInstanceInScope(new DbBackup(), withoutDisposalTracking: true);\n"})})})]}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsx)("div",{children:(0,i.jsxs)(s.p,{children:["You can also give your instance a name to use it like a ",(0,i.jsx)(s.a,{href:"/docs/guides/basics#named-registration",children:"named registration"}),":"]})}),(0,i.jsx)("div",{children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:'using var scope = container.BeginScope();\nscope.PutInstanceInScope(new DbBackup(), false, name: "DbBackup");\n'})})})]}),"\n",(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsxs)(s.p,{children:["Instances put to a scope will take precedence over existing registrations with the same ",(0,i.jsx)(s.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),"."]})}),"\n",(0,i.jsx)(s.h2,{id:"disposal",children:"Disposal"}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsxs)(s.p,{children:["The currently resolving scope tracks services that implement either ",(0,i.jsx)(s.code,{children:"IDisposable"})," or ",(0,i.jsx)(s.code,{children:"IAsyncDisposable"}),". This means that when the scope is disposed, all the tracked disposable instances will be disposed with it."]}),(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsx)(s.p,{children:"Disposing the container will dispose all the singleton instances and their dependencies."})})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(t.A,{groupId:"lifetime-dispose",children:[(0,i.jsx)(c.A,{value:"Using",label:"Using",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"using (var scope = container.BeginScope())\n{\n var disposable = scope.Resolve();\n} // 'disposable' will be disposed when \n // the using statement ends.\n"})})}),(0,i.jsx)(c.A,{value:"Dispose",label:"Dispose",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"var scope = container.BeginScope();\nvar disposable = scope.Resolve();\n\n// 'disposable' will be disposed with the scope.\nscope.Dispose();\n"})})})]})})]}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsx)("div",{children:(0,i.jsxs)(s.p,{children:["You can disable the disposal tracking on a ",(0,i.jsx)(s.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"service registration"})," with the ",(0,i.jsx)(s.code,{children:".WithoutDisposalTracking()"})," option."]})}),(0,i.jsx)("div",{children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"container.Register(options => \n options.WithoutDisposalTracking());\n"})})})]}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(s.h3,{id:"async-disposal",children:"Async disposal"}),(0,i.jsxs)(s.p,{children:["As the container and its scopes implement the ",(0,i.jsx)(s.code,{children:"IAsyncDisposable"})," interface, you can dispose them asynchronously when they are used in an ",(0,i.jsx)(s.code,{children:"async"})," context."]}),(0,i.jsxs)(s.p,{children:["Calling ",(0,i.jsx)(s.code,{children:"DisposeAsync"})," disposes both ",(0,i.jsx)(s.code,{children:"IDisposable"})," and ",(0,i.jsx)(s.code,{children:"IAsyncDisposable"})," instances; however, calling ",(0,i.jsx)(s.code,{children:"Dispose"})," only disposes ",(0,i.jsx)(s.code,{children:"IDisposable"})," instances."]})]}),(0,i.jsx)("div",{children:(0,i.jsxs)(t.A,{groupId:"lifetime-dispose",children:[(0,i.jsx)(c.A,{value:"Using",label:"Using",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"await using (var scope = container.BeginScope())\n{\n var disposable = scope.Resolve();\n} // 'disposable' will be disposed asynchronously \n // when the using statement ends.\n"})})}),(0,i.jsx)(c.A,{value:"Dispose",label:"Dispose",children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"var scope = container.BeginScope();\nvar disposable = scope.Resolve();\n\n// 'disposable' will be disposed asynchronously with the scope.\nawait scope.DisposeAsync();\n"})})})]})})]}),"\n",(0,i.jsxs)(a.A,{children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(s.h3,{id:"finalizer-delegate",children:"Finalizer delegate"}),(0,i.jsxs)(s.p,{children:["During ",(0,i.jsx)(s.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"service registration"}),", you can set a custom finalizer delegate that will be invoked at the service's disposal."]})]}),(0,i.jsx)("div",{children:(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-cs",children:"container.Register(options => \n options.WithFinalizer(backup => \n backup.CloseDbConnection()));\n"})})})]})]})}function b(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},9365:(e,s,n)=>{n.d(s,{A:()=>t});n(6540);var i=n(870);const o={tabItem:"tabItem_Ymn6"};var a=n(4848);function t(e){let{children:s,hidden:n,className:t}=e;return(0,a.jsx)("div",{role:"tabpanel",className:(0,i.A)(o.tabItem,t),hidden:n,children:s})}},1470:(e,s,n)=>{n.d(s,{A:()=>w});var i=n(6540),o=n(870),a=n(3104),t=n(6347),c=n(205),r=n(7485),l=n(1682),d=n(9466);function p(e){return i.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,i.isValidElement)(e)&&function(e){const{props:s}=e;return!!s&&"object"==typeof s&&"value"in s}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function u(e){const{values:s,children:n}=e;return(0,i.useMemo)((()=>{const e=s??function(e){return p(e).map((e=>{let{props:{value:s,label:n,attributes:i,default:o}}=e;return{value:s,label:n,attributes:i,default:o}}))}(n);return function(e){const s=(0,l.X)(e,((e,s)=>e.value===s.value));if(s.length>0)throw new Error(`Docusaurus error: Duplicate values "${s.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[s,n])}function h(e){let{value:s,tabValues:n}=e;return n.some((e=>e.value===s))}function b(e){let{queryString:s=!1,groupId:n}=e;const o=(0,t.W6)(),a=function(e){let{queryString:s=!1,groupId:n}=e;if("string"==typeof s)return s;if(!1===s)return null;if(!0===s&&!n)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:s,groupId:n});return[(0,r.aZ)(a),(0,i.useCallback)((e=>{if(!a)return;const s=new URLSearchParams(o.location.search);s.set(a,e),o.replace({...o.location,search:s.toString()})}),[a,o])]}function g(e){const{defaultValue:s,queryString:n=!1,groupId:o}=e,a=u(e),[t,r]=(0,i.useState)((()=>function(e){let{defaultValue:s,tabValues:n}=e;if(0===n.length)throw new Error("Docusaurus error: the component requires at least one children component");if(s){if(!h({value:s,tabValues:n}))throw new Error(`Docusaurus error: The has a defaultValue "${s}" but none of its children has the corresponding value. Available values are: ${n.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return s}const i=n.find((e=>e.default))??n[0];if(!i)throw new Error("Unexpected error: 0 tabValues");return i.value}({defaultValue:s,tabValues:a}))),[l,p]=b({queryString:n,groupId:o}),[g,m]=function(e){let{groupId:s}=e;const n=function(e){return e?`docusaurus.tab.${e}`:null}(s),[o,a]=(0,d.Dv)(n);return[o,(0,i.useCallback)((e=>{n&&a.set(e)}),[n,a])]}({groupId:o}),v=(()=>{const e=l??g;return h({value:e,tabValues:a})?e:null})();(0,c.A)((()=>{v&&r(v)}),[v]);return{selectedValue:t,selectValue:(0,i.useCallback)((e=>{if(!h({value:e,tabValues:a}))throw new Error(`Can't select invalid tab value=${e}`);r(e),p(e),m(e)}),[p,m,a]),tabValues:a}}var m=n(2303);const v={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var x=n(4848);function j(e){let{className:s,block:n,selectedValue:i,selectValue:t,tabValues:c}=e;const r=[],{blockElementScrollPositionUntilNextRender:l}=(0,a.a_)(),d=e=>{const s=e.currentTarget,n=r.indexOf(s),o=c[n].value;o!==i&&(l(s),t(o))},p=e=>{let s=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const n=r.indexOf(e.currentTarget)+1;s=r[n]??r[0];break}case"ArrowLeft":{const n=r.indexOf(e.currentTarget)-1;s=r[n]??r[r.length-1];break}}s?.focus()};return(0,x.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,o.A)("tabs",{"tabs--block":n},s),children:c.map((e=>{let{value:s,label:n,attributes:a}=e;return(0,x.jsx)("li",{role:"tab",tabIndex:i===s?0:-1,"aria-selected":i===s,ref:e=>r.push(e),onKeyDown:p,onClick:d,...a,className:(0,o.A)("tabs__item",v.tabItem,a?.className,{"tabs__item--active":i===s}),children:n??s},s)}))})}function f(e){let{lazy:s,children:n,selectedValue:o}=e;const a=(Array.isArray(n)?n:[n]).filter(Boolean);if(s){const e=a.find((e=>e.props.value===o));return e?(0,i.cloneElement)(e,{className:"margin-top--md"}):null}return(0,x.jsx)("div",{className:"margin-top--md",children:a.map(((e,s)=>(0,i.cloneElement)(e,{key:s,hidden:e.props.value!==o})))})}function y(e){const s=g(e);return(0,x.jsxs)("div",{className:(0,o.A)("tabs-container",v.tabList),children:[(0,x.jsx)(j,{...e,...s}),(0,x.jsx)(f,{...e,...s})]})}function w(e){const s=(0,m.A)();return(0,x.jsx)(y,{...e,children:p(e.children)},String(s))}},7470:(e,s,n)=>{n.d(s,{A:()=>t});var i=n(6540);const o={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var a=n(4848);function t(e){let{children:s}=e,n=i.Children.toArray(s).filter((e=>e));return(0,a.jsxs)("div",{className:o.codeDescContainer,children:[(0,a.jsx)("div",{className:o.desc,children:n[0]}),(0,a.jsx)("div",{className:o.example,children:n[1]})]})}},8453:(e,s,n)=>{n.d(s,{R:()=>t,x:()=>c});var i=n(6540);const o={},a=i.createContext(o);function t(e){const s=i.useContext(a);return i.useMemo((function(){return"function"==typeof e?e(s):{...s,...e}}),[s,e])}function c(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:t(e.components),i.createElement(a.Provider,{value:s},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/7a96ca3d.7f57d2b9.js b/assets/js/7a96ca3d.8d38ba06.js similarity index 99% rename from assets/js/7a96ca3d.7f57d2b9.js rename to assets/js/7a96ca3d.8d38ba06.js index e46f29b5..60f7edaa 100644 --- a/assets/js/7a96ca3d.7f57d2b9.js +++ b/assets/js/7a96ca3d.8d38ba06.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[271],{3726:(t,e,s)=>{s.r(e),s.d(e,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>a,toc:()=>h});var i=s(4848),n=s(8453);const r={title:"Overview"},o="Stashbox",a={id:"getting-started/overview",title:"Overview",description:"Appveyor Build Status",source:"@site/docs/getting-started/overview.md",sourceDirName:"getting-started",slug:"/getting-started/overview",permalink:"/stashbox/docs/getting-started/overview",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/getting-started/overview.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{title:"Overview"},sidebar:"docs",next:{title:"Introduction",permalink:"/stashbox/docs/getting-started/introduction"}},l={},h=[{value:"Core attributes",id:"core-attributes",level:2},{value:"Supported platforms",id:"supported-platforms",level:2},{value:"Contact & support",id:"contact--support",level:2},{value:"License",id:"license",level:2}];function c(t){const e={a:"a",h1:"h1",h2:"h2",img:"img",li:"li",p:"p",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,n.R)(),...t.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(e.h1,{id:"stashbox",children:"Stashbox"}),"\n",(0,i.jsxs)(e.p,{children:[(0,i.jsx)(e.a,{href:"https://ci.appveyor.com/project/pcsajtai/stashbox/branch/master",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/appveyor/build/pcsajtai/stashbox?logo=appveyor&logoColor=white",alt:"Appveyor Build Status"})}),"\n",(0,i.jsx)(e.a,{href:"https://github.com/z4kn4fein/stashbox/actions/workflows/linux-macOS-CI.yml",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/github/actions/workflow/status/z4kn4fein/stashbox/linux-macOS-CI.yml?logo=GitHub&branch=master",alt:"GitHub Workflow Status"})}),"\n",(0,i.jsx)(e.a,{href:"https://www.nuget.org/packages/Stashbox",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/nuget/dt/Stashbox?label=nuget",alt:"NuGet Downloads"})}),"\n",(0,i.jsx)(e.a,{href:"https://sonarcloud.io/project/overview?id=z4kn4fein_stashbox",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/sonar/tests/z4kn4fein_stashbox?compact_message&logo=sonarcloud&server=https%3A%2F%2Fsonarcloud.io",alt:"Sonar Tests"})}),"\n",(0,i.jsx)(e.a,{href:"https://sonarcloud.io/project/overview?id=z4kn4fein_stashbox",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/sonar/coverage/z4kn4fein_stashbox?logo=SonarCloud&server=https%3A%2F%2Fsonarcloud.io",alt:"Sonar Coverage"})}),"\n",(0,i.jsx)(e.a,{href:"https://sonarcloud.io/project/overview?id=z4kn4fein_stashbox",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/sonar/quality_gate/z4kn4fein_stashbox?logo=sonarcloud&server=https%3A%2F%2Fsonarcloud.io",alt:"Sonar Quality Gate"})}),"\n",(0,i.jsx)(e.a,{href:"https://github.com/dotnet/sourcelink",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/badge/sourcelink-enabled-brightgreen.svg",alt:"Sourcelink"})})]}),"\n",(0,i.jsx)(e.p,{children:"Stashbox is a lightweight, fast, and portable dependency injection framework for .NET-based solutions. It encourages the building of loosely coupled applications and simplifies the construction of hierarchical object structures. It can be integrated easily with .NET Core, Generic Host, ASP.NET, Xamarin, and many other applications."}),"\n",(0,i.jsx)(e.p,{children:"These are the latest available stable and pre-release versions:"}),"\n",(0,i.jsxs)(e.table,{children:[(0,i.jsx)(e.thead,{children:(0,i.jsxs)(e.tr,{children:[(0,i.jsx)(e.th,{children:"Github (stable)"}),(0,i.jsx)(e.th,{children:"NuGet (stable)"}),(0,i.jsx)(e.th,{children:"NuGet (daily)"})]})}),(0,i.jsx)(e.tbody,{children:(0,i.jsxs)(e.tr,{children:[(0,i.jsx)(e.td,{children:(0,i.jsx)(e.a,{href:"https://github.com/z4kn4fein/stashbox/releases",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/github/release/z4kn4fein/stashbox.svg",alt:"Github release"})})}),(0,i.jsx)(e.td,{children:(0,i.jsx)(e.a,{href:"https://www.nuget.org/packages/Stashbox",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/nuget/v/Stashbox",alt:"NuGet Version"})})}),(0,i.jsx)(e.td,{children:(0,i.jsx)(e.a,{href:"https://www.nuget.org/packages/Stashbox/",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/nuget/vpre/Stashbox",alt:"Nuget pre-release"})})})]})})]}),"\n",(0,i.jsx)(e.h2,{id:"core-attributes",children:"Core attributes"}),"\n",(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsx)(e.li,{children:"\ud83d\ude80 Fast, thread-safe, and lock-free operations."}),"\n",(0,i.jsx)(e.li,{children:"\u26a1\ufe0f Easy-to-use Fluent configuration API."}),"\n",(0,i.jsx)(e.li,{children:"\u267b\ufe0f Small memory footprint."}),"\n",(0,i.jsx)(e.li,{children:"\ud83d\udd04 Tracks the dependency tree for cycles."}),"\n",(0,i.jsx)(e.li,{children:"\ud83d\udea8 Detects and warns about misconfigurations."}),"\n",(0,i.jsx)(e.li,{children:"\ud83d\udd25 Gives fast feedback on registration/resolution issues."}),"\n"]}),"\n",(0,i.jsx)(e.h2,{id:"supported-platforms",children:"Supported platforms"}),"\n",(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsx)(e.li,{children:".NET 5+"}),"\n",(0,i.jsx)(e.li,{children:".NET Standard 2.0+"}),"\n",(0,i.jsx)(e.li,{children:".NET Framework 4.5+"}),"\n",(0,i.jsx)(e.li,{children:"Mono"}),"\n",(0,i.jsx)(e.li,{children:"Universal Windows Platform"}),"\n",(0,i.jsx)(e.li,{children:"Xamarin (Android/iOS/Mac)"}),"\n",(0,i.jsx)(e.li,{children:"Unity"}),"\n"]}),"\n",(0,i.jsx)(e.h2,{id:"contact--support",children:"Contact & support"}),"\n",(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsxs)(e.li,{children:[(0,i.jsx)(e.a,{href:"https://gitter.im/z4kn4fein/stashbox",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/gitter/room/z4kn4fein/stashbox.svg",alt:"Join the chat at https://gitter.im/z4kn4fein/stashbox"})})," ",(0,i.jsx)(e.a,{href:"https://3vj.short.gy/stashbox-slack",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/badge/chat-on%20slack-orange.svg?style=flat",alt:"Slack"})})]}),"\n",(0,i.jsxs)(e.li,{children:["Create a ",(0,i.jsx)(e.a,{href:"https://github.com/z4kn4fein/stashbox/issues",children:"GitHub issue"})," for bug reports and feature requests."]}),"\n",(0,i.jsxs)(e.li,{children:["Start a ",(0,i.jsx)(e.a,{href:"https://github.com/z4kn4fein/stashbox/discussions",children:"GitHub discussion"})," for your questions and ideas."]}),"\n",(0,i.jsxs)(e.li,{children:["Add a \u2b50\ufe0f ",(0,i.jsx)(e.a,{href:"https://github.com/z4kn4fein/stashbox",children:"on GitHub"})," to support the project!"]}),"\n"]}),"\n",(0,i.jsx)(e.h2,{id:"license",children:"License"}),"\n",(0,i.jsxs)(e.p,{children:["This project is licensed under the ",(0,i.jsx)(e.a,{href:"https://github.com/z4kn4fein/stashbox/blob/master/LICENSE",children:"MIT license"}),"."]})]})}function d(t={}){const{wrapper:e}={...(0,n.R)(),...t.components};return e?(0,i.jsx)(e,{...t,children:(0,i.jsx)(c,{...t})}):c(t)}},8453:(t,e,s)=>{s.d(e,{R:()=>o,x:()=>a});var i=s(6540);const n={},r=i.createContext(n);function o(t){const e=i.useContext(r);return i.useMemo((function(){return"function"==typeof t?t(e):{...e,...t}}),[e,t])}function a(t){let e;return e=t.disableParentContext?"function"==typeof t.components?t.components(n):t.components||n:o(t.components),i.createElement(r.Provider,{value:e},t.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[271],{3726:(t,e,s)=>{s.r(e),s.d(e,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>a,toc:()=>h});var i=s(4848),n=s(8453);const r={title:"Overview"},o="Stashbox",a={id:"getting-started/overview",title:"Overview",description:"Appveyor Build Status",source:"@site/docs/getting-started/overview.md",sourceDirName:"getting-started",slug:"/getting-started/overview",permalink:"/stashbox/docs/getting-started/overview",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/getting-started/overview.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{title:"Overview"},sidebar:"docs",next:{title:"Introduction",permalink:"/stashbox/docs/getting-started/introduction"}},l={},h=[{value:"Core attributes",id:"core-attributes",level:2},{value:"Supported platforms",id:"supported-platforms",level:2},{value:"Contact & support",id:"contact--support",level:2},{value:"License",id:"license",level:2}];function c(t){const e={a:"a",h1:"h1",h2:"h2",img:"img",li:"li",p:"p",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,n.R)(),...t.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(e.h1,{id:"stashbox",children:"Stashbox"}),"\n",(0,i.jsxs)(e.p,{children:[(0,i.jsx)(e.a,{href:"https://ci.appveyor.com/project/pcsajtai/stashbox/branch/master",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/appveyor/build/pcsajtai/stashbox?logo=appveyor&logoColor=white",alt:"Appveyor Build Status"})}),"\n",(0,i.jsx)(e.a,{href:"https://github.com/z4kn4fein/stashbox/actions/workflows/linux-macOS-CI.yml",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/github/actions/workflow/status/z4kn4fein/stashbox/linux-macOS-CI.yml?logo=GitHub&branch=master",alt:"GitHub Workflow Status"})}),"\n",(0,i.jsx)(e.a,{href:"https://www.nuget.org/packages/Stashbox",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/nuget/dt/Stashbox?label=nuget",alt:"NuGet Downloads"})}),"\n",(0,i.jsx)(e.a,{href:"https://sonarcloud.io/project/overview?id=z4kn4fein_stashbox",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/sonar/tests/z4kn4fein_stashbox?compact_message&logo=sonarcloud&server=https%3A%2F%2Fsonarcloud.io",alt:"Sonar Tests"})}),"\n",(0,i.jsx)(e.a,{href:"https://sonarcloud.io/project/overview?id=z4kn4fein_stashbox",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/sonar/coverage/z4kn4fein_stashbox?logo=SonarCloud&server=https%3A%2F%2Fsonarcloud.io",alt:"Sonar Coverage"})}),"\n",(0,i.jsx)(e.a,{href:"https://sonarcloud.io/project/overview?id=z4kn4fein_stashbox",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/sonar/quality_gate/z4kn4fein_stashbox?logo=sonarcloud&server=https%3A%2F%2Fsonarcloud.io",alt:"Sonar Quality Gate"})}),"\n",(0,i.jsx)(e.a,{href:"https://github.com/dotnet/sourcelink",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/badge/sourcelink-enabled-brightgreen.svg",alt:"Sourcelink"})})]}),"\n",(0,i.jsx)(e.p,{children:"Stashbox is a lightweight, fast, and portable dependency injection framework for .NET-based solutions. It encourages the building of loosely coupled applications and simplifies the construction of hierarchical object structures. It can be integrated easily with .NET Core, Generic Host, ASP.NET, Xamarin, and many other applications."}),"\n",(0,i.jsx)(e.p,{children:"These are the latest available stable and pre-release versions:"}),"\n",(0,i.jsxs)(e.table,{children:[(0,i.jsx)(e.thead,{children:(0,i.jsxs)(e.tr,{children:[(0,i.jsx)(e.th,{children:"Github (stable)"}),(0,i.jsx)(e.th,{children:"NuGet (stable)"}),(0,i.jsx)(e.th,{children:"NuGet (daily)"})]})}),(0,i.jsx)(e.tbody,{children:(0,i.jsxs)(e.tr,{children:[(0,i.jsx)(e.td,{children:(0,i.jsx)(e.a,{href:"https://github.com/z4kn4fein/stashbox/releases",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/github/release/z4kn4fein/stashbox.svg",alt:"Github release"})})}),(0,i.jsx)(e.td,{children:(0,i.jsx)(e.a,{href:"https://www.nuget.org/packages/Stashbox",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/nuget/v/Stashbox",alt:"NuGet Version"})})}),(0,i.jsx)(e.td,{children:(0,i.jsx)(e.a,{href:"https://www.nuget.org/packages/Stashbox/",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/nuget/vpre/Stashbox",alt:"Nuget pre-release"})})})]})})]}),"\n",(0,i.jsx)(e.h2,{id:"core-attributes",children:"Core attributes"}),"\n",(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsx)(e.li,{children:"\ud83d\ude80 Fast, thread-safe, and lock-free operations."}),"\n",(0,i.jsx)(e.li,{children:"\u26a1\ufe0f Easy-to-use Fluent configuration API."}),"\n",(0,i.jsx)(e.li,{children:"\u267b\ufe0f Small memory footprint."}),"\n",(0,i.jsx)(e.li,{children:"\ud83d\udd04 Tracks the dependency tree for cycles."}),"\n",(0,i.jsx)(e.li,{children:"\ud83d\udea8 Detects and warns about misconfigurations."}),"\n",(0,i.jsx)(e.li,{children:"\ud83d\udd25 Gives fast feedback on registration/resolution issues."}),"\n"]}),"\n",(0,i.jsx)(e.h2,{id:"supported-platforms",children:"Supported platforms"}),"\n",(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsx)(e.li,{children:".NET 5+"}),"\n",(0,i.jsx)(e.li,{children:".NET Standard 2.0+"}),"\n",(0,i.jsx)(e.li,{children:".NET Framework 4.5+"}),"\n",(0,i.jsx)(e.li,{children:"Mono"}),"\n",(0,i.jsx)(e.li,{children:"Universal Windows Platform"}),"\n",(0,i.jsx)(e.li,{children:"Xamarin (Android/iOS/Mac)"}),"\n",(0,i.jsx)(e.li,{children:"Unity"}),"\n"]}),"\n",(0,i.jsx)(e.h2,{id:"contact--support",children:"Contact & support"}),"\n",(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsxs)(e.li,{children:[(0,i.jsx)(e.a,{href:"https://gitter.im/z4kn4fein/stashbox",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/gitter/room/z4kn4fein/stashbox.svg",alt:"Join the chat at https://gitter.im/z4kn4fein/stashbox"})})," ",(0,i.jsx)(e.a,{href:"https://3vj.short.gy/stashbox-slack",children:(0,i.jsx)(e.img,{src:"https://img.shields.io/badge/chat-on%20slack-orange.svg?style=flat",alt:"Slack"})})]}),"\n",(0,i.jsxs)(e.li,{children:["Create a ",(0,i.jsx)(e.a,{href:"https://github.com/z4kn4fein/stashbox/issues",children:"GitHub issue"})," for bug reports and feature requests."]}),"\n",(0,i.jsxs)(e.li,{children:["Start a ",(0,i.jsx)(e.a,{href:"https://github.com/z4kn4fein/stashbox/discussions",children:"GitHub discussion"})," for your questions and ideas."]}),"\n",(0,i.jsxs)(e.li,{children:["Add a \u2b50\ufe0f ",(0,i.jsx)(e.a,{href:"https://github.com/z4kn4fein/stashbox",children:"on GitHub"})," to support the project!"]}),"\n"]}),"\n",(0,i.jsx)(e.h2,{id:"license",children:"License"}),"\n",(0,i.jsxs)(e.p,{children:["This project is licensed under the ",(0,i.jsx)(e.a,{href:"https://github.com/z4kn4fein/stashbox/blob/master/LICENSE",children:"MIT license"}),"."]})]})}function d(t={}){const{wrapper:e}={...(0,n.R)(),...t.components};return e?(0,i.jsx)(e,{...t,children:(0,i.jsx)(c,{...t})}):c(t)}},8453:(t,e,s)=>{s.d(e,{R:()=>o,x:()=>a});var i=s(6540);const n={},r=i.createContext(n);function o(t){const e=i.useContext(r);return i.useMemo((function(){return"function"==typeof t?t(e):{...e,...t}}),[e,t])}function a(t){let e;return e=t.disableParentContext?"function"==typeof t.components?t.components(n):t.components||n:o(t.components),i.createElement(r.Provider,{value:e},t.children)}}}]); \ No newline at end of file diff --git a/assets/js/851a7a9e.4e8777b3.js b/assets/js/851a7a9e.c87840f5.js similarity index 99% rename from assets/js/851a7a9e.4e8777b3.js rename to assets/js/851a7a9e.c87840f5.js index 94bcb090..cb16ae72 100644 --- a/assets/js/851a7a9e.4e8777b3.js +++ b/assets/js/851a7a9e.c87840f5.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[878],{7863:(e,n,r)=>{r.r(n),r.d(n,{assets:()=>v,contentTitle:()=>l,default:()=>p,frontMatter:()=>c,metadata:()=>d,toc:()=>h});var t=r(4848),o=r(8453),s=r(7470),a=r(1470),i=r(9365);const c={},l="Decorators",d={id:"advanced/decorators",title:"Decorators",description:"Stashbox supports decorator service registration to take advantage of the Decorator pattern. This pattern is used to extend the functionality of a class without changing its implementation. This is also what the Open\u2013closed principle stands for; services should be open for extension but closed for modification.",source:"@site/docs/advanced/decorators.md",sourceDirName:"advanced",slug:"/advanced/decorators",permalink:"/stashbox/docs/advanced/decorators",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/advanced/decorators.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Generics",permalink:"/stashbox/docs/advanced/generics"},next:{title:"Wrappers & resolvers",permalink:"/stashbox/docs/advanced/wrappers-resolvers"}},v={},h=[{value:"Simple use-case",id:"simple-use-case",level:2},{value:"Multiple decorators",id:"multiple-decorators",level:2},{value:"Conditional decoration",id:"conditional-decoration",level:2},{value:"Generic decorators",id:"generic-decorators",level:2},{value:"Composite pattern",id:"composite-pattern",level:2},{value:"Decorating multiple services",id:"decorating-multiple-services",level:2},{value:"Lifetime",id:"lifetime",level:2},{value:"Wrappers",id:"wrappers",level:2},{value:"Interception",id:"interception",level:2}];function u(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,o.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{id:"decorators",children:"Decorators"}),"\n",(0,t.jsxs)(n.p,{children:["Stashbox supports decorator ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"service registration"})," to take advantage of the ",(0,t.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Decorator_pattern",children:"Decorator pattern"}),". This pattern is used to extend the functionality of a class without changing its implementation. This is also what the ",(0,t.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle",children:"Open\u2013closed principle"})," stands for; services should be open for extension but closed for modification."]}),"\n",(0,t.jsx)(n.h2,{id:"simple-use-case",children:"Simple use-case"}),"\n",(0,t.jsxs)(n.p,{children:["We define an ",(0,t.jsx)(n.code,{children:"IEventProcessor"})," service used to process ",(0,t.jsx)(n.code,{children:"Event"})," entities. Then we'll decorate this service with additional validation capabilities:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"class Event { }\nclass UpdateEvent : Event { }\n\ninterface IEventProcessor\n{\n void ProcessEvent(Event @event);\n}\n\ninterface IEventValidator\n{\n bool IsValid(Event @event);\n}\n\nclass EventValidator : IEventValidator\n{\n public bool IsValid(Event @event) { /* do the actual validation. */ }\n}\n\nclass GeneralEventProcessor : IEventProcessor\n{\n public void ProcessEvent(Event @event)\n {\n // suppose this method is processing the given event.\n this.DoTheActualProcessing(@event);\n }\n}\n\nclass ValidatorProcessor : IEventProcessor\n{\n private readonly IEventProcessor nextProcessor;\n private readonly IEventValidator eventValidator;\n public ValidatorProcessor(IEventProcessor eventProcessor, IEventValidator eventValidator)\n {\n this.nextProcessor = eventProcessor;\n this.eventValidator = eventValidator;\n }\n\n public void ProcessEvent(Event @event)\n {\n // validate the event first.\n if (!this.eventValidator.IsValid(@event))\n throw new InvalidEventException();\n\n // if everything is ok, call the next processor.\n this.nextProcessor.ProcessEvent(@event);\n }\n}\n\nusing var container = new StashboxContainer();\n\ncontainer.Register();\ncontainer.Register();\ncontainer.RegisterDecorator();\n\n// new ValidatorProcessor(new GeneralEventProcessor(), new EventValidator())\nvar eventProcessor = container.Resolve();\n\n// process the event.\neventProcessor.ProcessEvent(new UpdateEvent());\n"})}),"\n",(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.code,{children:"GeneralEventProcessor"})," is an implementation of ",(0,t.jsx)(n.code,{children:"IEventProcessor"})," and does the actual event processing logic. It does not have any other responsibilities. Rather than putting the event validation's burden onto its shoulder, we create a different service for validation purposes. Instead of injecting the validator into the ",(0,t.jsx)(n.code,{children:"GeneralEventProcessor"})," directly, we let another ",(0,t.jsx)(n.code,{children:"IEventProcessor"})," decorate it like an ",(0,t.jsx)(n.em,{children:"event processing pipeline"})," that validates the event as a first step."]}),"\n",(0,t.jsx)(n.h2,{id:"multiple-decorators",children:"Multiple decorators"}),"\n",(0,t.jsxs)(s.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"You have the option to register multiple decorators for a service to extend its functionality."}),(0,t.jsx)(n.p,{children:"The decoration order will be the same as the registration order of the decorators. The first registered decorator will decorate the service itself. Then, all the subsequent decorators will wrap the already decorated service."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\ncontainer.RegisterDecorator();\ncontainer.RegisterDecorator();\n\n// new ValidatorProcessor(new LoggerProcessor(new GeneralProcessor()));\nvar processor = container.Resolve(); \n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"conditional-decoration",children:"Conditional decoration"}),"\n",(0,t.jsxs)(n.p,{children:["With ",(0,t.jsx)(n.a,{href:"/docs/guides/service-resolution#conditional-resolution",children:"conditional resolution"})," you can control which decorator should be selected to decorate a given service."]}),"\n",(0,t.jsxs)(a.A,{children:[(0,t.jsxs)(i.A,{value:"Decoretee",label:"Decoretee",children:[(0,t.jsxs)(n.p,{children:["You have the option to set which decorator should be selected for a given implementation. For a single type filter, you can use the ",(0,t.jsx)(n.code,{children:".WhenDecoratedServiceIs()"})," configuration option. To select more types, you can use the more generic ",(0,t.jsx)(n.code,{children:".When()"})," option."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\ncontainer.Register();\n\ncontainer.RegisterDecorator(options => options\n // select when CustomProcessor or GeneralProcessor is resolved.\n .WhenDecoratedServiceIs()\n .WhenDecoratedServiceIs());\n\ncontainer.RegisterDecorator(options => options\n // select only when GeneralProcessor is resolved.\n .WhenDecoratedServiceIs());\n\n// [\n// new ValidatorProcessor(new LoggerProcessor(new GeneralProcessor())),\n// new LoggerProcessor(new CustomProcessor())\n// ]\nvar processors = container.ResolveAll();\n"})})]}),(0,t.jsxs)(i.A,{value:"Named",label:"Named",children:[(0,t.jsx)(n.p,{children:"You can filter for service names to control the decorator selection."}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.Register("General");\ncontainer.Register("Custom");\n\ncontainer.RegisterDecorator(options => options\n // select when CustomProcessor or GeneralProcessor is resolved.\n .WhenDecoratedServiceIs("General")\n .WhenDecoratedServiceIs("Custom"));\n\ncontainer.RegisterDecorator(options => options\n // select only when GeneralProcessor is resolved.\n .WhenDecoratedServiceIs("General"));\n\n// new ValidatorProcessor(new LoggerProcessor(new GeneralProcessor()))\nvar general = container.Resolve("General");\n\n// new LoggerProcessor(new CustomProcessor())\nvar custom = container.Resolve("Custom");\n'})})]}),(0,t.jsxs)(i.A,{value:"Attribute",label:"Attribute",children:[(0,t.jsxs)(n.p,{children:["You can use your custom attributes to control the decorator selection. With ",(0,t.jsx)(n.strong,{children:"class attributes"}),", you can mark your classes for decoration."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"class LogAttribute : Attribute { }\nclass ValidateAttribute : Attribute { }\n\n[Log, Validate]\nclass GeneralProcessor : IEventProcessor { }\n\n[Log]\nclass CustomProcessor : IEventProcessor { }\n\ncontainer.Register();\ncontainer.Register();\n\ncontainer.RegisterDecorator(options => options\n // select when the resolving class has 'LogAttribute'.\n .WhenDecoratedServiceHas());\n\ncontainer.RegisterDecorator(options => options\n // select when the resolving class has 'ValidateAttribute'.\n .WhenDecoratedServiceHas());\n\n// [\n// new ValidatorProcessor(new LoggerProcessor(new GeneralProcessor())),\n// new LoggerProcessor(new CustomProcessor())\n// ]\nvar processors = container.ResolveAll();\n"})}),(0,t.jsxs)(n.p,{children:["You can also mark your dependencies for decoration with ",(0,t.jsx)(n.strong,{children:"property / field / parameter attributes"}),"."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"class LogAttribute : Attribute { }\nclass ValidateAttribute : Attribute { }\n\nclass ProcessorExecutor\n{\n public ProcessorExecutor([Log, Validate]IEventProcessor eventProcessor)\n { }\n}\n\ncontainer.Register();\ncontainer.Register();\n\ncontainer.RegisterDecorator(options => options\n // select when the resolving dependency has 'LogAttribute'.\n .WhenHas());\n\ncontainer.RegisterDecorator(options => options\n // select when the resolving dependency has 'ValidateAttribute'.\n .WhenHas());\n\n// new ProcessorExecutor(new ValidatorProcessor(new LoggerProcessor(new GeneralProcessor())))\nvar executor = container.ResolveAll();\n"})})]})]}),"\n",(0,t.jsx)(n.h2,{id:"generic-decorators",children:"Generic decorators"}),"\n",(0,t.jsxs)(n.p,{children:["Stashbox supports the registration of open-generic decorators, which allows the extension of open-generic services.\nInspection of ",(0,t.jsx)(n.a,{href:"/docs/advanced/generics#generic-constraints",children:"generic parameter constraints"})," and ",(0,t.jsx)(n.a,{href:"/docs/advanced/generics#variance",children:"variance handling"})," is supported on generic decorators also."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"interface IEventProcessor\n{\n void ProcessEvent(TEvent @event);\n}\n\nclass GeneralEventProcessor : IEventProcessor\n{\n public void ProcessEvent(TEvent @event) { /* suppose this method is processing the given event.*/ }\n}\n\nclass ValidatorProcessor : IEventProcessor\n{\n private readonly IEventProcessor nextProcessor;\n\n public ValidatorProcessor(IEventProcessor eventProcessor)\n {\n this.nextProcessor = eventProcessor;\n }\n\n public void ProcessEvent(TEvent @event)\n {\n // validate the event first.\n if (!this.IsValid(@event))\n throw new InvalidEventException();\n\n // if everything is ok, call the next processor.\n this.nextProcessor.ProcessEvent(@event);\n }\n}\n\nusing var container = new StashboxContainer();\ncontainer.Register(typeof(IEventProcessor<>), typeof(GeneralEventProcessor<>));\ncontainer.RegisterDecorator(typeof(IEventProcessor<>), typeof(ValidatorProcessor<>));\n\n// new ValidatorProcessor(new GeneralEventProcessor())\nvar eventProcessor = container.Resolve>();\n\n// process the event.\neventProcessor.ProcessEvent(new UpdateEvent());\n"})}),"\n",(0,t.jsx)(n.h2,{id:"composite-pattern",children:"Composite pattern"}),"\n",(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Composite_pattern",children:"Composite pattern"})," allows a group of objects to be treated the same way as a single instance of the same type. It's useful when you want to use the functionality of multiple instances behind the same interface. You can achieve this by registering a decorator that takes a collection of the same service as a dependency."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"public class CompositeValidator : IEventValidator\n{\n private readonly IEnumerable> validators;\n\n public CompositeValidator(IEnumerable> validators)\n {\n this.validators = validators;\n }\n\n public bool IsValid(TEvent @event)\n {\n return this.validators.All(validator => validator.IsValid(@event));\n }\n}\n\ncontainer.Register(typeof(IEventValidator<>), typeof(EventValidator<>));\ncontainer.Register(typeof(IEventValidator<>), typeof(AnotherEventValidator<>));\ncontainer.RegisterDecorator(typeof(IEventValidator<>), typeof(CompositeValidator<>));\n"})}),"\n",(0,t.jsx)(n.h2,{id:"decorating-multiple-services",children:"Decorating multiple services"}),"\n",(0,t.jsxs)(n.p,{children:["You have the option to organize similar decorating functionalities for different interfaces into the same decorator class.\nIn this example, we would like to validate a given ",(0,t.jsx)(n.code,{children:"Event"})," right before publishing and also before processing."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"public class EventValidator : IEventProcessor, IEventPublisher\n{\n private readonly IEventProcessor processor;\n private readonly IEventPublisher publisher;\n private readonly IEventValidator validator;\n\n public CompositeValidator(IEventProcessor processor, \n IEventPublisher publisher, \n IEventValidator validator)\n {\n this.processor = processor;\n this.publisher = publisher;\n this.validator = validator;\n }\n\n public void ProcessEvent(TEvent @event)\n {\n // validate the event first.\n if (!this.validator.IsValid(@event))\n throw new InvalidEventException();\n\n // if everything is ok, call the processor.\n this.processor.ProcessEvent(@event);\n }\n\n public void PublishEvent(TEvent @event)\n {\n // validate the event first.\n if (!this.validator.IsValid(@event))\n throw new InvalidEventException();\n\n // if everything is ok, call the publisher.\n this.publisher.PublishEvent(@event);\n }\n}\n\ncontainer.Register(typeof(IEventProcessor<>), typeof(EventProcessor<>));\ncontainer.Register(typeof(IEventPublisher<>), typeof(EventPublisher<>));\ncontainer.Register(typeof(IEventValidator<>), typeof(EventValidator<>));\n\n// without specifying the interface type, the container binds this registration to all of its implemented types\ncontainer.RegisterDecorator(typeof(EventValidator<>));\n"})}),"\n",(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["You can also use the ",(0,t.jsx)(n.a,{href:"/docs/guides/advanced-registration#binding-to-multiple-services",children:"Binding to multiple services"})," options."]})}),"\n",(0,t.jsx)(n.h2,{id:"lifetime",children:"Lifetime"}),"\n",(0,t.jsxs)(s.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"Just as other registrations, decorators also can have their lifetime. It means, in addition to the service's lifetime, all decorator's lifetime will be applied to the wrapped service."}),(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsx)(n.p,{children:"When you don't set a decorator's lifetime, it'll implicitly inherit the decorated service's lifetime."})})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\n// singleton decorator will change the transient \n// decorated service's lifetime to singleton.\ncontainer.RegisterDecorator(options => \n options.WithLifetime(Lifetimes.Singleton));\n// Singleton[new ValidatorProcessor()](Transien[new GeneralEventProcessor()]) \nvar processor = container.Resolve(); \n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"wrappers",children:"Wrappers"}),"\n",(0,t.jsxs)(s.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["Decorators are also applied to wrapped services. It means, in addition to the decoration, you can wrap your services in supported ",(0,t.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#wrappers",children:"wrappers"}),"."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\ncontainer.RegisterDecorator();\n// () => new ValidatorProcessor(new GeneralEventProcessor())\nvar processor = container.Resolve>();\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"interception",children:"Interception"}),"\n",(0,t.jsxs)(n.p,{children:["From the combination of Stashbox's decorator support and ",(0,t.jsx)(n.a,{href:"http://www.castleproject.org/projects/dynamicproxy/",children:"Castle DynamicProxy's"})," proxy generator, we can take advantage of the ",(0,t.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Aspect-oriented_programming",children:"Aspect-Oriented Programming's"})," benefits. The following example defines a ",(0,t.jsx)(n.code,{children:"LoggingInterceptor"})," that will log additional messages related to the called service methods."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'public class LoggingInterceptor : IInterceptor \n{\n private readonly ILogger logger;\n\n public LoggingInterceptor(ILogger logger)\n {\n this.logger = logger;\n }\n\n public void Intercept(IInvocation invocation)\n {\n var stopwatch = new Stopwatch();\n stopwatch.Start();\n\n // log before we invoke the intercepted method.\n this.logger.Log($"Method begin: {invocation.GetConcreteMethod().Name}");\n\n // call the intercepted method.\n invocation.Proceed();\n\n // log after we invoked the intercepted method and print how long it ran.\n this.logger.Log($"Method end: {invocation.GetConcreteMethod().Name}, execution duration: {stopwatch.ElapsedMiliseconds} ms");\n }\n}\n\n// create a DefaultProxyBuilder from the DynamicProxy library.\nvar proxyBuilder = new DefaultProxyBuilder();\n\n// build a proxy for the IEventProcessor interface.\nvar eventProcessorProxy = proxyBuilder.CreateInterfaceProxyTypeWithTargetInterface(\n typeof(IEventProcessor), \n new Type[0], \n ProxyGenerationOptions.Default);\n\n// register the logger for LoggingInterceptor.\ncontainer.Register();\n\n// register the service that we will intercept.\ncontainer.Register();\n\n// register the interceptor.\ncontainer.Register();\n\n// register the built proxy as a decorator.\ncontainer.RegisterDecorator(eventProcessorProxy);\n'})})]})}function p(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(u,{...e})}):u(e)}},9365:(e,n,r)=>{r.d(n,{A:()=>a});r(6540);var t=r(870);const o={tabItem:"tabItem_Ymn6"};var s=r(4848);function a(e){let{children:n,hidden:r,className:a}=e;return(0,s.jsx)("div",{role:"tabpanel",className:(0,t.A)(o.tabItem,a),hidden:r,children:n})}},1470:(e,n,r)=>{r.d(n,{A:()=>I});var t=r(6540),o=r(870),s=r(3104),a=r(6347),i=r(205),c=r(7485),l=r(1682),d=r(9466);function v(e){return t.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,t.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:r}=e;return(0,t.useMemo)((()=>{const e=n??function(e){return v(e).map((e=>{let{props:{value:n,label:r,attributes:t,default:o}}=e;return{value:n,label:r,attributes:t,default:o}}))}(r);return function(e){const n=(0,l.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,r])}function u(e){let{value:n,tabValues:r}=e;return r.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:r}=e;const o=(0,a.W6)(),s=function(e){let{queryString:n=!1,groupId:r}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!r)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return r??null}({queryString:n,groupId:r});return[(0,c.aZ)(s),(0,t.useCallback)((e=>{if(!s)return;const n=new URLSearchParams(o.location.search);n.set(s,e),o.replace({...o.location,search:n.toString()})}),[s,o])]}function g(e){const{defaultValue:n,queryString:r=!1,groupId:o}=e,s=h(e),[a,c]=(0,t.useState)((()=>function(e){let{defaultValue:n,tabValues:r}=e;if(0===r.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!u({value:n,tabValues:r}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${r.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const t=r.find((e=>e.default))??r[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:s}))),[l,v]=p({queryString:r,groupId:o}),[g,f]=function(e){let{groupId:n}=e;const r=function(e){return e?`docusaurus.tab.${e}`:null}(n),[o,s]=(0,d.Dv)(r);return[o,(0,t.useCallback)((e=>{r&&s.set(e)}),[r,s])]}({groupId:o}),m=(()=>{const e=l??g;return u({value:e,tabValues:s})?e:null})();(0,i.A)((()=>{m&&c(m)}),[m]);return{selectedValue:a,selectValue:(0,t.useCallback)((e=>{if(!u({value:e,tabValues:s}))throw new Error(`Can't select invalid tab value=${e}`);c(e),v(e),f(e)}),[v,f,s]),tabValues:s}}var f=r(2303);const m={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var P=r(4848);function E(e){let{className:n,block:r,selectedValue:t,selectValue:a,tabValues:i}=e;const c=[],{blockElementScrollPositionUntilNextRender:l}=(0,s.a_)(),d=e=>{const n=e.currentTarget,r=c.indexOf(n),o=i[r].value;o!==t&&(l(n),a(o))},v=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const r=c.indexOf(e.currentTarget)+1;n=c[r]??c[0];break}case"ArrowLeft":{const r=c.indexOf(e.currentTarget)-1;n=c[r]??c[c.length-1];break}}n?.focus()};return(0,P.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,o.A)("tabs",{"tabs--block":r},n),children:i.map((e=>{let{value:n,label:r,attributes:s}=e;return(0,P.jsx)("li",{role:"tab",tabIndex:t===n?0:-1,"aria-selected":t===n,ref:e=>c.push(e),onKeyDown:v,onClick:d,...s,className:(0,o.A)("tabs__item",m.tabItem,s?.className,{"tabs__item--active":t===n}),children:r??n},n)}))})}function x(e){let{lazy:n,children:r,selectedValue:o}=e;const s=(Array.isArray(r)?r:[r]).filter(Boolean);if(n){const e=s.find((e=>e.props.value===o));return e?(0,t.cloneElement)(e,{className:"margin-top--md"}):null}return(0,P.jsx)("div",{className:"margin-top--md",children:s.map(((e,n)=>(0,t.cloneElement)(e,{key:n,hidden:e.props.value!==o})))})}function b(e){const n=g(e);return(0,P.jsxs)("div",{className:(0,o.A)("tabs-container",m.tabList),children:[(0,P.jsx)(E,{...e,...n}),(0,P.jsx)(x,{...e,...n})]})}function I(e){const n=(0,f.A)();return(0,P.jsx)(b,{...e,children:v(e.children)},String(n))}},7470:(e,n,r)=>{r.d(n,{A:()=>a});var t=r(6540);const o={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var s=r(4848);function a(e){let{children:n}=e,r=t.Children.toArray(n).filter((e=>e));return(0,s.jsxs)("div",{className:o.codeDescContainer,children:[(0,s.jsx)("div",{className:o.desc,children:r[0]}),(0,s.jsx)("div",{className:o.example,children:r[1]})]})}},8453:(e,n,r)=>{r.d(n,{R:()=>a,x:()=>i});var t=r(6540);const o={},s=t.createContext(o);function a(e){const n=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:a(e.components),t.createElement(s.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[878],{7863:(e,n,r)=>{r.r(n),r.d(n,{assets:()=>v,contentTitle:()=>l,default:()=>p,frontMatter:()=>c,metadata:()=>d,toc:()=>h});var t=r(4848),o=r(8453),s=r(7470),a=r(1470),i=r(9365);const c={},l="Decorators",d={id:"advanced/decorators",title:"Decorators",description:"Stashbox supports decorator service registration to take advantage of the Decorator pattern. This pattern is used to extend the functionality of a class without changing its implementation. This is also what the Open\u2013closed principle stands for; services should be open for extension but closed for modification.",source:"@site/docs/advanced/decorators.md",sourceDirName:"advanced",slug:"/advanced/decorators",permalink:"/stashbox/docs/advanced/decorators",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/advanced/decorators.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Generics",permalink:"/stashbox/docs/advanced/generics"},next:{title:"Wrappers & resolvers",permalink:"/stashbox/docs/advanced/wrappers-resolvers"}},v={},h=[{value:"Simple use-case",id:"simple-use-case",level:2},{value:"Multiple decorators",id:"multiple-decorators",level:2},{value:"Conditional decoration",id:"conditional-decoration",level:2},{value:"Generic decorators",id:"generic-decorators",level:2},{value:"Composite pattern",id:"composite-pattern",level:2},{value:"Decorating multiple services",id:"decorating-multiple-services",level:2},{value:"Lifetime",id:"lifetime",level:2},{value:"Wrappers",id:"wrappers",level:2},{value:"Interception",id:"interception",level:2}];function u(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,o.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{id:"decorators",children:"Decorators"}),"\n",(0,t.jsxs)(n.p,{children:["Stashbox supports decorator ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"service registration"})," to take advantage of the ",(0,t.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Decorator_pattern",children:"Decorator pattern"}),". This pattern is used to extend the functionality of a class without changing its implementation. This is also what the ",(0,t.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle",children:"Open\u2013closed principle"})," stands for; services should be open for extension but closed for modification."]}),"\n",(0,t.jsx)(n.h2,{id:"simple-use-case",children:"Simple use-case"}),"\n",(0,t.jsxs)(n.p,{children:["We define an ",(0,t.jsx)(n.code,{children:"IEventProcessor"})," service used to process ",(0,t.jsx)(n.code,{children:"Event"})," entities. Then we'll decorate this service with additional validation capabilities:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"class Event { }\nclass UpdateEvent : Event { }\n\ninterface IEventProcessor\n{\n void ProcessEvent(Event @event);\n}\n\ninterface IEventValidator\n{\n bool IsValid(Event @event);\n}\n\nclass EventValidator : IEventValidator\n{\n public bool IsValid(Event @event) { /* do the actual validation. */ }\n}\n\nclass GeneralEventProcessor : IEventProcessor\n{\n public void ProcessEvent(Event @event)\n {\n // suppose this method is processing the given event.\n this.DoTheActualProcessing(@event);\n }\n}\n\nclass ValidatorProcessor : IEventProcessor\n{\n private readonly IEventProcessor nextProcessor;\n private readonly IEventValidator eventValidator;\n public ValidatorProcessor(IEventProcessor eventProcessor, IEventValidator eventValidator)\n {\n this.nextProcessor = eventProcessor;\n this.eventValidator = eventValidator;\n }\n\n public void ProcessEvent(Event @event)\n {\n // validate the event first.\n if (!this.eventValidator.IsValid(@event))\n throw new InvalidEventException();\n\n // if everything is ok, call the next processor.\n this.nextProcessor.ProcessEvent(@event);\n }\n}\n\nusing var container = new StashboxContainer();\n\ncontainer.Register();\ncontainer.Register();\ncontainer.RegisterDecorator();\n\n// new ValidatorProcessor(new GeneralEventProcessor(), new EventValidator())\nvar eventProcessor = container.Resolve();\n\n// process the event.\neventProcessor.ProcessEvent(new UpdateEvent());\n"})}),"\n",(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.code,{children:"GeneralEventProcessor"})," is an implementation of ",(0,t.jsx)(n.code,{children:"IEventProcessor"})," and does the actual event processing logic. It does not have any other responsibilities. Rather than putting the event validation's burden onto its shoulder, we create a different service for validation purposes. Instead of injecting the validator into the ",(0,t.jsx)(n.code,{children:"GeneralEventProcessor"})," directly, we let another ",(0,t.jsx)(n.code,{children:"IEventProcessor"})," decorate it like an ",(0,t.jsx)(n.em,{children:"event processing pipeline"})," that validates the event as a first step."]}),"\n",(0,t.jsx)(n.h2,{id:"multiple-decorators",children:"Multiple decorators"}),"\n",(0,t.jsxs)(s.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"You have the option to register multiple decorators for a service to extend its functionality."}),(0,t.jsx)(n.p,{children:"The decoration order will be the same as the registration order of the decorators. The first registered decorator will decorate the service itself. Then, all the subsequent decorators will wrap the already decorated service."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\ncontainer.RegisterDecorator();\ncontainer.RegisterDecorator();\n\n// new ValidatorProcessor(new LoggerProcessor(new GeneralProcessor()));\nvar processor = container.Resolve(); \n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"conditional-decoration",children:"Conditional decoration"}),"\n",(0,t.jsxs)(n.p,{children:["With ",(0,t.jsx)(n.a,{href:"/docs/guides/service-resolution#conditional-resolution",children:"conditional resolution"})," you can control which decorator should be selected to decorate a given service."]}),"\n",(0,t.jsxs)(a.A,{children:[(0,t.jsxs)(i.A,{value:"Decoretee",label:"Decoretee",children:[(0,t.jsxs)(n.p,{children:["You have the option to set which decorator should be selected for a given implementation. For a single type filter, you can use the ",(0,t.jsx)(n.code,{children:".WhenDecoratedServiceIs()"})," configuration option. To select more types, you can use the more generic ",(0,t.jsx)(n.code,{children:".When()"})," option."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\ncontainer.Register();\n\ncontainer.RegisterDecorator(options => options\n // select when CustomProcessor or GeneralProcessor is resolved.\n .WhenDecoratedServiceIs()\n .WhenDecoratedServiceIs());\n\ncontainer.RegisterDecorator(options => options\n // select only when GeneralProcessor is resolved.\n .WhenDecoratedServiceIs());\n\n// [\n// new ValidatorProcessor(new LoggerProcessor(new GeneralProcessor())),\n// new LoggerProcessor(new CustomProcessor())\n// ]\nvar processors = container.ResolveAll();\n"})})]}),(0,t.jsxs)(i.A,{value:"Named",label:"Named",children:[(0,t.jsx)(n.p,{children:"You can filter for service names to control the decorator selection."}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.Register("General");\ncontainer.Register("Custom");\n\ncontainer.RegisterDecorator(options => options\n // select when CustomProcessor or GeneralProcessor is resolved.\n .WhenDecoratedServiceIs("General")\n .WhenDecoratedServiceIs("Custom"));\n\ncontainer.RegisterDecorator(options => options\n // select only when GeneralProcessor is resolved.\n .WhenDecoratedServiceIs("General"));\n\n// new ValidatorProcessor(new LoggerProcessor(new GeneralProcessor()))\nvar general = container.Resolve("General");\n\n// new LoggerProcessor(new CustomProcessor())\nvar custom = container.Resolve("Custom");\n'})})]}),(0,t.jsxs)(i.A,{value:"Attribute",label:"Attribute",children:[(0,t.jsxs)(n.p,{children:["You can use your custom attributes to control the decorator selection. With ",(0,t.jsx)(n.strong,{children:"class attributes"}),", you can mark your classes for decoration."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"class LogAttribute : Attribute { }\nclass ValidateAttribute : Attribute { }\n\n[Log, Validate]\nclass GeneralProcessor : IEventProcessor { }\n\n[Log]\nclass CustomProcessor : IEventProcessor { }\n\ncontainer.Register();\ncontainer.Register();\n\ncontainer.RegisterDecorator(options => options\n // select when the resolving class has 'LogAttribute'.\n .WhenDecoratedServiceHas());\n\ncontainer.RegisterDecorator(options => options\n // select when the resolving class has 'ValidateAttribute'.\n .WhenDecoratedServiceHas());\n\n// [\n// new ValidatorProcessor(new LoggerProcessor(new GeneralProcessor())),\n// new LoggerProcessor(new CustomProcessor())\n// ]\nvar processors = container.ResolveAll();\n"})}),(0,t.jsxs)(n.p,{children:["You can also mark your dependencies for decoration with ",(0,t.jsx)(n.strong,{children:"property / field / parameter attributes"}),"."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"class LogAttribute : Attribute { }\nclass ValidateAttribute : Attribute { }\n\nclass ProcessorExecutor\n{\n public ProcessorExecutor([Log, Validate]IEventProcessor eventProcessor)\n { }\n}\n\ncontainer.Register();\ncontainer.Register();\n\ncontainer.RegisterDecorator(options => options\n // select when the resolving dependency has 'LogAttribute'.\n .WhenHas());\n\ncontainer.RegisterDecorator(options => options\n // select when the resolving dependency has 'ValidateAttribute'.\n .WhenHas());\n\n// new ProcessorExecutor(new ValidatorProcessor(new LoggerProcessor(new GeneralProcessor())))\nvar executor = container.ResolveAll();\n"})})]})]}),"\n",(0,t.jsx)(n.h2,{id:"generic-decorators",children:"Generic decorators"}),"\n",(0,t.jsxs)(n.p,{children:["Stashbox supports the registration of open-generic decorators, which allows the extension of open-generic services.\nInspection of ",(0,t.jsx)(n.a,{href:"/docs/advanced/generics#generic-constraints",children:"generic parameter constraints"})," and ",(0,t.jsx)(n.a,{href:"/docs/advanced/generics#variance",children:"variance handling"})," is supported on generic decorators also."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"interface IEventProcessor\n{\n void ProcessEvent(TEvent @event);\n}\n\nclass GeneralEventProcessor : IEventProcessor\n{\n public void ProcessEvent(TEvent @event) { /* suppose this method is processing the given event.*/ }\n}\n\nclass ValidatorProcessor : IEventProcessor\n{\n private readonly IEventProcessor nextProcessor;\n\n public ValidatorProcessor(IEventProcessor eventProcessor)\n {\n this.nextProcessor = eventProcessor;\n }\n\n public void ProcessEvent(TEvent @event)\n {\n // validate the event first.\n if (!this.IsValid(@event))\n throw new InvalidEventException();\n\n // if everything is ok, call the next processor.\n this.nextProcessor.ProcessEvent(@event);\n }\n}\n\nusing var container = new StashboxContainer();\ncontainer.Register(typeof(IEventProcessor<>), typeof(GeneralEventProcessor<>));\ncontainer.RegisterDecorator(typeof(IEventProcessor<>), typeof(ValidatorProcessor<>));\n\n// new ValidatorProcessor(new GeneralEventProcessor())\nvar eventProcessor = container.Resolve>();\n\n// process the event.\neventProcessor.ProcessEvent(new UpdateEvent());\n"})}),"\n",(0,t.jsx)(n.h2,{id:"composite-pattern",children:"Composite pattern"}),"\n",(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Composite_pattern",children:"Composite pattern"})," allows a group of objects to be treated the same way as a single instance of the same type. It's useful when you want to use the functionality of multiple instances behind the same interface. You can achieve this by registering a decorator that takes a collection of the same service as a dependency."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"public class CompositeValidator : IEventValidator\n{\n private readonly IEnumerable> validators;\n\n public CompositeValidator(IEnumerable> validators)\n {\n this.validators = validators;\n }\n\n public bool IsValid(TEvent @event)\n {\n return this.validators.All(validator => validator.IsValid(@event));\n }\n}\n\ncontainer.Register(typeof(IEventValidator<>), typeof(EventValidator<>));\ncontainer.Register(typeof(IEventValidator<>), typeof(AnotherEventValidator<>));\ncontainer.RegisterDecorator(typeof(IEventValidator<>), typeof(CompositeValidator<>));\n"})}),"\n",(0,t.jsx)(n.h2,{id:"decorating-multiple-services",children:"Decorating multiple services"}),"\n",(0,t.jsxs)(n.p,{children:["You have the option to organize similar decorating functionalities for different interfaces into the same decorator class.\nIn this example, we would like to validate a given ",(0,t.jsx)(n.code,{children:"Event"})," right before publishing and also before processing."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"public class EventValidator : IEventProcessor, IEventPublisher\n{\n private readonly IEventProcessor processor;\n private readonly IEventPublisher publisher;\n private readonly IEventValidator validator;\n\n public CompositeValidator(IEventProcessor processor, \n IEventPublisher publisher, \n IEventValidator validator)\n {\n this.processor = processor;\n this.publisher = publisher;\n this.validator = validator;\n }\n\n public void ProcessEvent(TEvent @event)\n {\n // validate the event first.\n if (!this.validator.IsValid(@event))\n throw new InvalidEventException();\n\n // if everything is ok, call the processor.\n this.processor.ProcessEvent(@event);\n }\n\n public void PublishEvent(TEvent @event)\n {\n // validate the event first.\n if (!this.validator.IsValid(@event))\n throw new InvalidEventException();\n\n // if everything is ok, call the publisher.\n this.publisher.PublishEvent(@event);\n }\n}\n\ncontainer.Register(typeof(IEventProcessor<>), typeof(EventProcessor<>));\ncontainer.Register(typeof(IEventPublisher<>), typeof(EventPublisher<>));\ncontainer.Register(typeof(IEventValidator<>), typeof(EventValidator<>));\n\n// without specifying the interface type, the container binds this registration to all of its implemented types\ncontainer.RegisterDecorator(typeof(EventValidator<>));\n"})}),"\n",(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["You can also use the ",(0,t.jsx)(n.a,{href:"/docs/guides/advanced-registration#binding-to-multiple-services",children:"Binding to multiple services"})," options."]})}),"\n",(0,t.jsx)(n.h2,{id:"lifetime",children:"Lifetime"}),"\n",(0,t.jsxs)(s.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"Just as other registrations, decorators also can have their lifetime. It means, in addition to the service's lifetime, all decorator's lifetime will be applied to the wrapped service."}),(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsx)(n.p,{children:"When you don't set a decorator's lifetime, it'll implicitly inherit the decorated service's lifetime."})})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\n// singleton decorator will change the transient \n// decorated service's lifetime to singleton.\ncontainer.RegisterDecorator(options => \n options.WithLifetime(Lifetimes.Singleton));\n// Singleton[new ValidatorProcessor()](Transien[new GeneralEventProcessor()]) \nvar processor = container.Resolve(); \n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"wrappers",children:"Wrappers"}),"\n",(0,t.jsxs)(s.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["Decorators are also applied to wrapped services. It means, in addition to the decoration, you can wrap your services in supported ",(0,t.jsx)(n.a,{href:"/docs/advanced/wrappers-resolvers#wrappers",children:"wrappers"}),"."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\ncontainer.RegisterDecorator();\n// () => new ValidatorProcessor(new GeneralEventProcessor())\nvar processor = container.Resolve>();\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"interception",children:"Interception"}),"\n",(0,t.jsxs)(n.p,{children:["From the combination of Stashbox's decorator support and ",(0,t.jsx)(n.a,{href:"http://www.castleproject.org/projects/dynamicproxy/",children:"Castle DynamicProxy's"})," proxy generator, we can take advantage of the ",(0,t.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Aspect-oriented_programming",children:"Aspect-Oriented Programming's"})," benefits. The following example defines a ",(0,t.jsx)(n.code,{children:"LoggingInterceptor"})," that will log additional messages related to the called service methods."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'public class LoggingInterceptor : IInterceptor \n{\n private readonly ILogger logger;\n\n public LoggingInterceptor(ILogger logger)\n {\n this.logger = logger;\n }\n\n public void Intercept(IInvocation invocation)\n {\n var stopwatch = new Stopwatch();\n stopwatch.Start();\n\n // log before we invoke the intercepted method.\n this.logger.Log($"Method begin: {invocation.GetConcreteMethod().Name}");\n\n // call the intercepted method.\n invocation.Proceed();\n\n // log after we invoked the intercepted method and print how long it ran.\n this.logger.Log($"Method end: {invocation.GetConcreteMethod().Name}, execution duration: {stopwatch.ElapsedMiliseconds} ms");\n }\n}\n\n// create a DefaultProxyBuilder from the DynamicProxy library.\nvar proxyBuilder = new DefaultProxyBuilder();\n\n// build a proxy for the IEventProcessor interface.\nvar eventProcessorProxy = proxyBuilder.CreateInterfaceProxyTypeWithTargetInterface(\n typeof(IEventProcessor), \n new Type[0], \n ProxyGenerationOptions.Default);\n\n// register the logger for LoggingInterceptor.\ncontainer.Register();\n\n// register the service that we will intercept.\ncontainer.Register();\n\n// register the interceptor.\ncontainer.Register();\n\n// register the built proxy as a decorator.\ncontainer.RegisterDecorator(eventProcessorProxy);\n'})})]})}function p(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(u,{...e})}):u(e)}},9365:(e,n,r)=>{r.d(n,{A:()=>a});r(6540);var t=r(870);const o={tabItem:"tabItem_Ymn6"};var s=r(4848);function a(e){let{children:n,hidden:r,className:a}=e;return(0,s.jsx)("div",{role:"tabpanel",className:(0,t.A)(o.tabItem,a),hidden:r,children:n})}},1470:(e,n,r)=>{r.d(n,{A:()=>I});var t=r(6540),o=r(870),s=r(3104),a=r(6347),i=r(205),c=r(7485),l=r(1682),d=r(9466);function v(e){return t.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,t.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:r}=e;return(0,t.useMemo)((()=>{const e=n??function(e){return v(e).map((e=>{let{props:{value:n,label:r,attributes:t,default:o}}=e;return{value:n,label:r,attributes:t,default:o}}))}(r);return function(e){const n=(0,l.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,r])}function u(e){let{value:n,tabValues:r}=e;return r.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:r}=e;const o=(0,a.W6)(),s=function(e){let{queryString:n=!1,groupId:r}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!r)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return r??null}({queryString:n,groupId:r});return[(0,c.aZ)(s),(0,t.useCallback)((e=>{if(!s)return;const n=new URLSearchParams(o.location.search);n.set(s,e),o.replace({...o.location,search:n.toString()})}),[s,o])]}function g(e){const{defaultValue:n,queryString:r=!1,groupId:o}=e,s=h(e),[a,c]=(0,t.useState)((()=>function(e){let{defaultValue:n,tabValues:r}=e;if(0===r.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!u({value:n,tabValues:r}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${r.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const t=r.find((e=>e.default))??r[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:s}))),[l,v]=p({queryString:r,groupId:o}),[g,f]=function(e){let{groupId:n}=e;const r=function(e){return e?`docusaurus.tab.${e}`:null}(n),[o,s]=(0,d.Dv)(r);return[o,(0,t.useCallback)((e=>{r&&s.set(e)}),[r,s])]}({groupId:o}),m=(()=>{const e=l??g;return u({value:e,tabValues:s})?e:null})();(0,i.A)((()=>{m&&c(m)}),[m]);return{selectedValue:a,selectValue:(0,t.useCallback)((e=>{if(!u({value:e,tabValues:s}))throw new Error(`Can't select invalid tab value=${e}`);c(e),v(e),f(e)}),[v,f,s]),tabValues:s}}var f=r(2303);const m={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var P=r(4848);function E(e){let{className:n,block:r,selectedValue:t,selectValue:a,tabValues:i}=e;const c=[],{blockElementScrollPositionUntilNextRender:l}=(0,s.a_)(),d=e=>{const n=e.currentTarget,r=c.indexOf(n),o=i[r].value;o!==t&&(l(n),a(o))},v=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const r=c.indexOf(e.currentTarget)+1;n=c[r]??c[0];break}case"ArrowLeft":{const r=c.indexOf(e.currentTarget)-1;n=c[r]??c[c.length-1];break}}n?.focus()};return(0,P.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,o.A)("tabs",{"tabs--block":r},n),children:i.map((e=>{let{value:n,label:r,attributes:s}=e;return(0,P.jsx)("li",{role:"tab",tabIndex:t===n?0:-1,"aria-selected":t===n,ref:e=>c.push(e),onKeyDown:v,onClick:d,...s,className:(0,o.A)("tabs__item",m.tabItem,s?.className,{"tabs__item--active":t===n}),children:r??n},n)}))})}function x(e){let{lazy:n,children:r,selectedValue:o}=e;const s=(Array.isArray(r)?r:[r]).filter(Boolean);if(n){const e=s.find((e=>e.props.value===o));return e?(0,t.cloneElement)(e,{className:"margin-top--md"}):null}return(0,P.jsx)("div",{className:"margin-top--md",children:s.map(((e,n)=>(0,t.cloneElement)(e,{key:n,hidden:e.props.value!==o})))})}function b(e){const n=g(e);return(0,P.jsxs)("div",{className:(0,o.A)("tabs-container",m.tabList),children:[(0,P.jsx)(E,{...e,...n}),(0,P.jsx)(x,{...e,...n})]})}function I(e){const n=(0,f.A)();return(0,P.jsx)(b,{...e,children:v(e.children)},String(n))}},7470:(e,n,r)=>{r.d(n,{A:()=>a});var t=r(6540);const o={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var s=r(4848);function a(e){let{children:n}=e,r=t.Children.toArray(n).filter((e=>e));return(0,s.jsxs)("div",{className:o.codeDescContainer,children:[(0,s.jsx)("div",{className:o.desc,children:r[0]}),(0,s.jsx)("div",{className:o.example,children:r[1]})]})}},8453:(e,n,r)=>{r.d(n,{R:()=>a,x:()=>i});var t=r(6540);const o={},s=t.createContext(o);function a(e){const n=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:a(e.components),t.createElement(s.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/9ff4038f.0e0ce6cd.js b/assets/js/9ff4038f.72dcabb1.js similarity index 94% rename from assets/js/9ff4038f.0e0ce6cd.js rename to assets/js/9ff4038f.72dcabb1.js index 3fb121eb..1325f2a6 100644 --- a/assets/js/9ff4038f.0e0ce6cd.js +++ b/assets/js/9ff4038f.72dcabb1.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[289],{8225:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>c,default:()=>p,frontMatter:()=>i,metadata:()=>l,toc:()=>u});var a=t(4848),r=t(8453),s=t(1470),o=t(9365);const i={title:"Introduction"},c=void 0,l={id:"getting-started/introduction",title:"Introduction",description:"Stashbox and its extensions are distributed via NuGet packages.",source:"@site/docs/getting-started/introduction.md",sourceDirName:"getting-started",slug:"/getting-started/introduction",permalink:"/stashbox/docs/getting-started/introduction",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/getting-started/introduction.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{title:"Introduction"},sidebar:"docs",previous:{title:"Overview",permalink:"/stashbox/docs/getting-started/overview"},next:{title:"Glossary",permalink:"/stashbox/docs/getting-started/glossary"}},d={},u=[{value:"Usage",id:"usage",level:2},{value:"How it works?",id:"how-it-works",level:2},{value:"Example",id:"example",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h2:"h2",li:"li",p:"p",pre:"pre",ul:"ul",...(0,r.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(n.p,{children:["Stashbox and its extensions are distributed via ",(0,a.jsx)(n.a,{href:"https://www.nuget.org/packages?q=stashbox",children:"NuGet"})," packages."]}),"\n",(0,a.jsxs)(s.A,{children:[(0,a.jsxs)(o.A,{value:"Package Manager",label:"Package Manager",children:[(0,a.jsx)(n.p,{children:"You can install the package by typing the following into the Package Manager Console:"}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-powershell",children:"Install-Package Stashbox -Version 5.16.0\n"})})]}),(0,a.jsxs)(o.A,{value:"dotnet CLI",label:"dotnet CLI",children:[(0,a.jsx)(n.p,{children:"You can install the package by using the dotnet cli:"}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:"dotnet add package Stashbox --version 5.16.0\n"})})]}),(0,a.jsxs)(o.A,{value:"PackageReference",label:"PackageReference",children:[(0,a.jsxs)(n.p,{children:["You can add the package into the package references of your ",(0,a.jsx)(n.code,{children:".csproj"}),":"]}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-xml",children:'\n'})})]})]}),"\n",(0,a.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,a.jsxs)(n.p,{children:["The general idea behind using Stashbox is that you structure your code with loosely coupled components with the ",(0,a.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Dependency_inversion_principle",children:"Dependency Inversion Principle"}),", ",(0,a.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Inversion_of_control",children:"Inversion Of Control"})," and ",(0,a.jsx)(n.a,{href:"https://martinfowler.com/articles/injection.html",children:"Dependency Injection"})," in mind."]}),"\n",(0,a.jsx)(n.p,{children:"Rather than letting the services instantiate their dependencies inside themselves, inject the dependencies on construction. Also, rather than creating the object hierarchy manually, you can use a Dependency Injection framework that does the work for you. That's why you are here, I suppose. \ud83d\ude42"}),"\n",(0,a.jsx)(n.p,{children:"To achieve the most efficient usage of Stashbox, you should follow these steps:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["At the startup of your application, instantiate a ",(0,a.jsx)(n.code,{children:"StashboxContainer"}),"."]}),"\n",(0,a.jsx)(n.li,{children:"Register your services into the container."}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.a,{href:"/docs/diagnostics/validation",children:"Validate"})," the state of the container and the registrations with the ",(0,a.jsx)(n.code,{children:".Validate()"})," method. ",(0,a.jsx)(n.em,{children:"(Optional)"})]}),"\n",(0,a.jsx)(n.li,{children:"During the lifetime of the application, use the container to resolve your services."}),"\n",(0,a.jsxs)(n.li,{children:["Create ",(0,a.jsx)(n.a,{href:"/docs/guides/scopes",children:"scopes"})," and use them to resolve your services. ",(0,a.jsx)(n.em,{children:"(Optional)"})]}),"\n",(0,a.jsxs)(n.li,{children:["On application exit, call the container's ",(0,a.jsx)(n.code,{children:".Dispose()"})," or ",(0,a.jsx)(n.code,{children:".DisposeAsync()"})," method to clean up the resources. ",(0,a.jsx)(n.em,{children:"(Optional)"})]}),"\n"]}),"\n",(0,a.jsx)(n.admonition,{type:"caution",children:(0,a.jsxs)(n.p,{children:["You should create only a single instance from ",(0,a.jsx)(n.code,{children:"StashboxContainer"})," (plus child containers if you use them) per application domain. ",(0,a.jsx)(n.code,{children:"StashboxContainer"})," instances are thread-safe. Do not create new container instances continuously, such action will bypass the container's internal delegate cache and could lead to performance degradation."]})}),"\n",(0,a.jsx)(n.h2,{id:"how-it-works",children:"How it works?"}),"\n",(0,a.jsxs)(n.p,{children:["Stashbox builds and maintains a collection of ",(0,a.jsx)(n.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"registered services"}),". When a service is requested for resolution, Stashbox starts looking for a matching registration that has the same ",(0,a.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," as the type that was requested. If it finds one, the container initiates a scan on the ",(0,a.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type's"})," available constructors and selects the one with the most arguments it knows how to resolve by matching argument types to other registrations."]}),"\n",(0,a.jsx)(n.p,{children:"When every constructor argument has a companion registration, Stashbox jumps to the first one and continues the same scanning operation."}),"\n",(0,a.jsxs)(n.p,{children:["This process is repeated until every ",(0,a.jsx)(n.a,{href:"/docs/getting-started/glossary#injectable-dependency",children:"injectable dependency"})," has a matching registration in the ",(0,a.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"}),". At the end of the process, Stashbox will have each dependency node built-up in a hierarchical object structure to instantiate the initially requested service object."]}),"\n",(0,a.jsx)(n.h2,{id:"example",children:"Example"}),"\n",(0,a.jsxs)(n.p,{children:["Let's see a quick example. We have three services ",(0,a.jsx)(n.code,{children:"DbBackup"}),", ",(0,a.jsx)(n.code,{children:"MessageBus"})," and ",(0,a.jsx)(n.code,{children:"ConsoleLogger"}),". ",(0,a.jsx)(n.code,{children:"DbBackup"})," has a dependency on ",(0,a.jsx)(n.code,{children:"IEventBroadcaster"})," (implemented by ",(0,a.jsx)(n.code,{children:"MessageBus"}),") and ",(0,a.jsx)(n.code,{children:"ILogger"})," (implemented by ",(0,a.jsx)(n.code,{children:"ConsoleLogger"}),"), ",(0,a.jsx)(n.code,{children:"MessageBus"})," also depending on an ",(0,a.jsx)(n.code,{children:"ILogger"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cs",children:'public interface IJob { void DoTheJob(); }\n\npublic interface ILogger { void Log(string message); }\n\npublic interface IEventBroadcaster { void Broadcast(IEvent @event); }\n\n\npublic class ConsoleLogger : ILogger\n{\n public void Log(string message) => Console.WriteLine(message);\n}\n\npublic class MessageBus : IEventBroadcaster\n{\n private readonly ILogger logger;\n\n public MessageBus(ILogger logger)\n {\n this.logger = logger;\n }\n\n void Broadcast(IEvent @event) \n {\n this.logger.Log($"Sending event to bus: {@event.Name}");\n // Do the actual event broadcast.\n }\n}\n\npublic class DbBackup : IJob\n{\n private readonly ILogger logger;\n private readonly IEventBroadcaster eventBroadcaster;\n\n public DbBackup(ILogger logger, IEventBroadcaster eventBroadcaster)\n {\n this.logger = logger;\n this.eventBroadcaster = eventBroadcaster;\n }\n\n public void DoTheJob() \n {\n this.logger.Log("Backing up!");\n // Do the actual backup.\n this.eventBroadcaster.Broadcast(new DbBackupCompleted());\n } \n}\n'})}),"\n",(0,a.jsx)(n.admonition,{type:"info",children:(0,a.jsx)(n.p,{children:"By depending only on interfaces, you decouple your services from concrete implementations. This gives you the flexibility of a more comfortable implementation replacement and also isolates your components from each other. For example, unit testing benefits a lot from the possibility of replacing real implementations with mocks."})}),"\n",(0,a.jsx)(n.p,{children:"The example above configured with Stashbox in a Console Application:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cs",children:"using Stashbox;\nusing System;\n\nnamespace Example\n{\n public class Program\n {\n private static readonly IStashboxContainer container;\n\n static Program()\n {\n // 1. Create container\n container = new StashboxContainer();\n\n // 2. Register your services\n container.RegisterSingleton();\n container.Register();\n container.Register();\n\n // 3. Validate the configuration.\n container.Validate();\n }\n\n static void Main(string[] args)\n {\n // 4. Resolve and use your service\n var job = container.Resolve();\n job.DoTheJob();\n }\n }\n}\n"})})]})}function p(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(h,{...e})}):h(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>o});t(6540);var a=t(870);const r={tabItem:"tabItem_Ymn6"};var s=t(4848);function o(e){let{children:n,hidden:t,className:o}=e;return(0,s.jsx)("div",{role:"tabpanel",className:(0,a.A)(r.tabItem,o),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>w});var a=t(6540),r=t(870),s=t(3104),o=t(6347),i=t(205),c=t(7485),l=t(1682),d=t(9466);function u(e){return a.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,a.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:t}=e;return(0,a.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:t,attributes:a,default:r}}=e;return{value:n,label:t,attributes:a,default:r}}))}(t);return function(e){const n=(0,l.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function p(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function g(e){let{queryString:n=!1,groupId:t}=e;const r=(0,o.W6)(),s=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,c.aZ)(s),(0,a.useCallback)((e=>{if(!s)return;const n=new URLSearchParams(r.location.search);n.set(s,e),r.replace({...r.location,search:n.toString()})}),[s,r])]}function m(e){const{defaultValue:n,queryString:t=!1,groupId:r}=e,s=h(e),[o,c]=(0,a.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const a=t.find((e=>e.default))??t[0];if(!a)throw new Error("Unexpected error: 0 tabValues");return a.value}({defaultValue:n,tabValues:s}))),[l,u]=g({queryString:t,groupId:r}),[m,b]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[r,s]=(0,d.Dv)(t);return[r,(0,a.useCallback)((e=>{t&&s.set(e)}),[t,s])]}({groupId:r}),v=(()=>{const e=l??m;return p({value:e,tabValues:s})?e:null})();(0,i.A)((()=>{v&&c(v)}),[v]);return{selectedValue:o,selectValue:(0,a.useCallback)((e=>{if(!p({value:e,tabValues:s}))throw new Error(`Can't select invalid tab value=${e}`);c(e),u(e),b(e)}),[u,b,s]),tabValues:s}}var b=t(2303);const v={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var f=t(4848);function x(e){let{className:n,block:t,selectedValue:a,selectValue:o,tabValues:i}=e;const c=[],{blockElementScrollPositionUntilNextRender:l}=(0,s.a_)(),d=e=>{const n=e.currentTarget,t=c.indexOf(n),r=i[t].value;r!==a&&(l(n),o(r))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const t=c.indexOf(e.currentTarget)+1;n=c[t]??c[0];break}case"ArrowLeft":{const t=c.indexOf(e.currentTarget)-1;n=c[t]??c[c.length-1];break}}n?.focus()};return(0,f.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":t},n),children:i.map((e=>{let{value:n,label:t,attributes:s}=e;return(0,f.jsx)("li",{role:"tab",tabIndex:a===n?0:-1,"aria-selected":a===n,ref:e=>c.push(e),onKeyDown:u,onClick:d,...s,className:(0,r.A)("tabs__item",v.tabItem,s?.className,{"tabs__item--active":a===n}),children:t??n},n)}))})}function j(e){let{lazy:n,children:t,selectedValue:r}=e;const s=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=s.find((e=>e.props.value===r));return e?(0,a.cloneElement)(e,{className:"margin-top--md"}):null}return(0,f.jsx)("div",{className:"margin-top--md",children:s.map(((e,n)=>(0,a.cloneElement)(e,{key:n,hidden:e.props.value!==r})))})}function y(e){const n=m(e);return(0,f.jsxs)("div",{className:(0,r.A)("tabs-container",v.tabList),children:[(0,f.jsx)(x,{...e,...n}),(0,f.jsx)(j,{...e,...n})]})}function w(e){const n=(0,b.A)();return(0,f.jsx)(y,{...e,children:u(e.children)},String(n))}},8453:(e,n,t)=>{t.d(n,{R:()=>o,x:()=>i});var a=t(6540);const r={},s=a.createContext(r);function o(e){const n=a.useContext(s);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),a.createElement(s.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[289],{8225:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>c,default:()=>p,frontMatter:()=>i,metadata:()=>l,toc:()=>u});var a=t(4848),r=t(8453),s=t(1470),o=t(9365);const i={title:"Introduction"},c=void 0,l={id:"getting-started/introduction",title:"Introduction",description:"Stashbox and its extensions are distributed via NuGet packages.",source:"@site/docs/getting-started/introduction.md",sourceDirName:"getting-started",slug:"/getting-started/introduction",permalink:"/stashbox/docs/getting-started/introduction",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/getting-started/introduction.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{title:"Introduction"},sidebar:"docs",previous:{title:"Overview",permalink:"/stashbox/docs/getting-started/overview"},next:{title:"Glossary",permalink:"/stashbox/docs/getting-started/glossary"}},d={},u=[{value:"Usage",id:"usage",level:2},{value:"How it works?",id:"how-it-works",level:2},{value:"Example",id:"example",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h2:"h2",li:"li",p:"p",pre:"pre",ul:"ul",...(0,r.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(n.p,{children:["Stashbox and its extensions are distributed via ",(0,a.jsx)(n.a,{href:"https://www.nuget.org/packages?q=stashbox",children:"NuGet"})," packages."]}),"\n",(0,a.jsxs)(s.A,{children:[(0,a.jsxs)(o.A,{value:"Package Manager",label:"Package Manager",children:[(0,a.jsx)(n.p,{children:"You can install the package by typing the following into the Package Manager Console:"}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-powershell",children:"Install-Package Stashbox -Version 5.16.0\n"})})]}),(0,a.jsxs)(o.A,{value:"dotnet CLI",label:"dotnet CLI",children:[(0,a.jsx)(n.p,{children:"You can install the package by using the dotnet cli:"}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:"dotnet add package Stashbox --version 5.16.0\n"})})]}),(0,a.jsxs)(o.A,{value:"PackageReference",label:"PackageReference",children:[(0,a.jsxs)(n.p,{children:["You can add the package into the package references of your ",(0,a.jsx)(n.code,{children:".csproj"}),":"]}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-xml",children:'\n'})})]})]}),"\n",(0,a.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,a.jsxs)(n.p,{children:["The general idea behind using Stashbox is that you structure your code with loosely coupled components with the ",(0,a.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Dependency_inversion_principle",children:"Dependency Inversion Principle"}),", ",(0,a.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Inversion_of_control",children:"Inversion Of Control"})," and ",(0,a.jsx)(n.a,{href:"https://martinfowler.com/articles/injection.html",children:"Dependency Injection"})," in mind."]}),"\n",(0,a.jsx)(n.p,{children:"Rather than letting the services instantiate their dependencies inside themselves, inject the dependencies on construction. Also, rather than creating the object hierarchy manually, you can use a Dependency Injection framework that does the work for you. That's why you are here, I suppose. \ud83d\ude42"}),"\n",(0,a.jsx)(n.p,{children:"To achieve the most efficient usage of Stashbox, you should follow these steps:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["At the startup of your application, instantiate a ",(0,a.jsx)(n.code,{children:"StashboxContainer"}),"."]}),"\n",(0,a.jsx)(n.li,{children:"Register your services into the container."}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.a,{href:"/docs/diagnostics/validation",children:"Validate"})," the state of the container and the registrations with the ",(0,a.jsx)(n.code,{children:".Validate()"})," method. ",(0,a.jsx)(n.em,{children:"(Optional)"})]}),"\n",(0,a.jsx)(n.li,{children:"During the lifetime of the application, use the container to resolve your services."}),"\n",(0,a.jsxs)(n.li,{children:["Create ",(0,a.jsx)(n.a,{href:"/docs/guides/scopes",children:"scopes"})," and use them to resolve your services. ",(0,a.jsx)(n.em,{children:"(Optional)"})]}),"\n",(0,a.jsxs)(n.li,{children:["On application exit, call the container's ",(0,a.jsx)(n.code,{children:".Dispose()"})," or ",(0,a.jsx)(n.code,{children:".DisposeAsync()"})," method to clean up the resources. ",(0,a.jsx)(n.em,{children:"(Optional)"})]}),"\n"]}),"\n",(0,a.jsx)(n.admonition,{type:"caution",children:(0,a.jsxs)(n.p,{children:["You should create only a single instance from ",(0,a.jsx)(n.code,{children:"StashboxContainer"})," (plus child containers if you use them) per application domain. ",(0,a.jsx)(n.code,{children:"StashboxContainer"})," instances are thread-safe. Do not create new container instances continuously, such action will bypass the container's internal delegate cache and could lead to performance degradation."]})}),"\n",(0,a.jsx)(n.h2,{id:"how-it-works",children:"How it works?"}),"\n",(0,a.jsxs)(n.p,{children:["Stashbox builds and maintains a collection of ",(0,a.jsx)(n.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"registered services"}),". When a service is requested for resolution, Stashbox starts looking for a matching registration that has the same ",(0,a.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," as the type that was requested. If it finds one, the container initiates a scan on the ",(0,a.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type's"})," available constructors and selects the one with the most arguments it knows how to resolve by matching argument types to other registrations."]}),"\n",(0,a.jsx)(n.p,{children:"When every constructor argument has a companion registration, Stashbox jumps to the first one and continues the same scanning operation."}),"\n",(0,a.jsxs)(n.p,{children:["This process is repeated until every ",(0,a.jsx)(n.a,{href:"/docs/getting-started/glossary#injectable-dependency",children:"injectable dependency"})," has a matching registration in the ",(0,a.jsx)(n.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"}),". At the end of the process, Stashbox will have each dependency node built-up in a hierarchical object structure to instantiate the initially requested service object."]}),"\n",(0,a.jsx)(n.h2,{id:"example",children:"Example"}),"\n",(0,a.jsxs)(n.p,{children:["Let's see a quick example. We have three services ",(0,a.jsx)(n.code,{children:"DbBackup"}),", ",(0,a.jsx)(n.code,{children:"MessageBus"})," and ",(0,a.jsx)(n.code,{children:"ConsoleLogger"}),". ",(0,a.jsx)(n.code,{children:"DbBackup"})," has a dependency on ",(0,a.jsx)(n.code,{children:"IEventBroadcaster"})," (implemented by ",(0,a.jsx)(n.code,{children:"MessageBus"}),") and ",(0,a.jsx)(n.code,{children:"ILogger"})," (implemented by ",(0,a.jsx)(n.code,{children:"ConsoleLogger"}),"), ",(0,a.jsx)(n.code,{children:"MessageBus"})," also depending on an ",(0,a.jsx)(n.code,{children:"ILogger"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cs",children:'public interface IJob { void DoTheJob(); }\n\npublic interface ILogger { void Log(string message); }\n\npublic interface IEventBroadcaster { void Broadcast(IEvent @event); }\n\n\npublic class ConsoleLogger : ILogger\n{\n public void Log(string message) => Console.WriteLine(message);\n}\n\npublic class MessageBus : IEventBroadcaster\n{\n private readonly ILogger logger;\n\n public MessageBus(ILogger logger)\n {\n this.logger = logger;\n }\n\n void Broadcast(IEvent @event) \n {\n this.logger.Log($"Sending event to bus: {@event.Name}");\n // Do the actual event broadcast.\n }\n}\n\npublic class DbBackup : IJob\n{\n private readonly ILogger logger;\n private readonly IEventBroadcaster eventBroadcaster;\n\n public DbBackup(ILogger logger, IEventBroadcaster eventBroadcaster)\n {\n this.logger = logger;\n this.eventBroadcaster = eventBroadcaster;\n }\n\n public void DoTheJob() \n {\n this.logger.Log("Backing up!");\n // Do the actual backup.\n this.eventBroadcaster.Broadcast(new DbBackupCompleted());\n } \n}\n'})}),"\n",(0,a.jsx)(n.admonition,{type:"info",children:(0,a.jsx)(n.p,{children:"By depending only on interfaces, you decouple your services from concrete implementations. This gives you the flexibility of a more comfortable implementation replacement and also isolates your components from each other. For example, unit testing benefits a lot from the possibility of replacing real implementations with mocks."})}),"\n",(0,a.jsx)(n.p,{children:"The example above configured with Stashbox in a Console Application:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cs",children:"using Stashbox;\nusing System;\n\nnamespace Example\n{\n public class Program\n {\n private static readonly IStashboxContainer container;\n\n static Program()\n {\n // 1. Create container\n container = new StashboxContainer();\n\n // 2. Register your services\n container.RegisterSingleton();\n container.Register();\n container.Register();\n\n // 3. Validate the configuration.\n container.Validate();\n }\n\n static void Main(string[] args)\n {\n // 4. Resolve and use your service\n var job = container.Resolve();\n job.DoTheJob();\n }\n }\n}\n"})})]})}function p(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(h,{...e})}):h(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>o});t(6540);var a=t(870);const r={tabItem:"tabItem_Ymn6"};var s=t(4848);function o(e){let{children:n,hidden:t,className:o}=e;return(0,s.jsx)("div",{role:"tabpanel",className:(0,a.A)(r.tabItem,o),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>w});var a=t(6540),r=t(870),s=t(3104),o=t(6347),i=t(205),c=t(7485),l=t(1682),d=t(9466);function u(e){return a.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,a.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:t}=e;return(0,a.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:t,attributes:a,default:r}}=e;return{value:n,label:t,attributes:a,default:r}}))}(t);return function(e){const n=(0,l.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function p(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function g(e){let{queryString:n=!1,groupId:t}=e;const r=(0,o.W6)(),s=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,c.aZ)(s),(0,a.useCallback)((e=>{if(!s)return;const n=new URLSearchParams(r.location.search);n.set(s,e),r.replace({...r.location,search:n.toString()})}),[s,r])]}function m(e){const{defaultValue:n,queryString:t=!1,groupId:r}=e,s=h(e),[o,c]=(0,a.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const a=t.find((e=>e.default))??t[0];if(!a)throw new Error("Unexpected error: 0 tabValues");return a.value}({defaultValue:n,tabValues:s}))),[l,u]=g({queryString:t,groupId:r}),[m,b]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[r,s]=(0,d.Dv)(t);return[r,(0,a.useCallback)((e=>{t&&s.set(e)}),[t,s])]}({groupId:r}),f=(()=>{const e=l??m;return p({value:e,tabValues:s})?e:null})();(0,i.A)((()=>{f&&c(f)}),[f]);return{selectedValue:o,selectValue:(0,a.useCallback)((e=>{if(!p({value:e,tabValues:s}))throw new Error(`Can't select invalid tab value=${e}`);c(e),u(e),b(e)}),[u,b,s]),tabValues:s}}var b=t(2303);const f={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var v=t(4848);function x(e){let{className:n,block:t,selectedValue:a,selectValue:o,tabValues:i}=e;const c=[],{blockElementScrollPositionUntilNextRender:l}=(0,s.a_)(),d=e=>{const n=e.currentTarget,t=c.indexOf(n),r=i[t].value;r!==a&&(l(n),o(r))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const t=c.indexOf(e.currentTarget)+1;n=c[t]??c[0];break}case"ArrowLeft":{const t=c.indexOf(e.currentTarget)-1;n=c[t]??c[c.length-1];break}}n?.focus()};return(0,v.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":t},n),children:i.map((e=>{let{value:n,label:t,attributes:s}=e;return(0,v.jsx)("li",{role:"tab",tabIndex:a===n?0:-1,"aria-selected":a===n,ref:e=>c.push(e),onKeyDown:u,onClick:d,...s,className:(0,r.A)("tabs__item",f.tabItem,s?.className,{"tabs__item--active":a===n}),children:t??n},n)}))})}function j(e){let{lazy:n,children:t,selectedValue:r}=e;const s=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=s.find((e=>e.props.value===r));return e?(0,a.cloneElement)(e,{className:"margin-top--md"}):null}return(0,v.jsx)("div",{className:"margin-top--md",children:s.map(((e,n)=>(0,a.cloneElement)(e,{key:n,hidden:e.props.value!==r})))})}function y(e){const n=m(e);return(0,v.jsxs)("div",{className:(0,r.A)("tabs-container",f.tabList),children:[(0,v.jsx)(x,{...e,...n}),(0,v.jsx)(j,{...e,...n})]})}function w(e){const n=(0,b.A)();return(0,v.jsx)(y,{...e,children:u(e.children)},String(n))}},8453:(e,n,t)=>{t.d(n,{R:()=>o,x:()=>i});var a=t(6540);const r={},s=a.createContext(r);function o(e){const n=a.useContext(s);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),a.createElement(s.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/a1e0d343.44070a57.js b/assets/js/a1e0d343.62369d9d.js similarity index 98% rename from assets/js/a1e0d343.44070a57.js rename to assets/js/a1e0d343.62369d9d.js index 1a4dcbf3..b0580cfa 100644 --- a/assets/js/a1e0d343.44070a57.js +++ b/assets/js/a1e0d343.62369d9d.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[494],{9:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>c,default:()=>g,frontMatter:()=>o,metadata:()=>d,toc:()=>h});var r=t(4848),s=t(8453),i=t(7470),a=t(1470),l=t(9365);const o={},c="Utilities",d={id:"diagnostics/utilities",title:"Utilities",description:"Is registered?",source:"@site/docs/diagnostics/utilities.md",sourceDirName:"diagnostics",slug:"/diagnostics/utilities",permalink:"/stashbox/docs/diagnostics/utilities",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/diagnostics/utilities.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Validation",permalink:"/stashbox/docs/diagnostics/validation"}},u={},h=[{value:"Is registered?",id:"is-registered",level:2},{value:"Named",id:"named",level:4},{value:"Can resolve?",id:"can-resolve",level:2},{value:"Get all mappings",id:"get-all-mappings",level:2},{value:"Registration diagnostics",id:"registration-diagnostics",level:2}];function p(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h1,{id:"utilities",children:"Utilities"}),"\n",(0,r.jsx)(n.h2,{id:"is-registered",children:"Is registered?"}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsxs)("div",{children:[(0,r.jsxs)(n.p,{children:["With the ",(0,r.jsx)(n.code,{children:"IsRegistered()"})," function, you can find out whether a service is registered into the container or not."]}),(0,r.jsxs)(n.p,{children:["It returns ",(0,r.jsx)(n.code,{children:"true"})," only when the container has a registration with the given type (and name). It only checks the actual container's registrations. For every cases, you should use the ",(0,r.jsx)(n.code,{children:"CanResolve()"})," method."]})]}),(0,r.jsx)("div",{children:(0,r.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,r.jsx)(l.A,{value:"Generic API",label:"Generic API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"bool isIJobRegistered = container.IsRegistered();\n"})})}),(0,r.jsx)(l.A,{value:"Runtime type API",label:"Runtime type API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"bool isIJobRegistered = container.IsRegistered(typeof(IJob));\n"})})}),(0,r.jsxs)(l.A,{value:"Named",label:"Named",children:[(0,r.jsx)(n.h4,{id:"named",children:(0,r.jsx)(n.strong,{children:"Named"})}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:'bool isIJobRegistered = container.IsRegistered("DbBackup");\n'})})]})]})})]}),"\n",(0,r.jsx)(n.h2,{id:"can-resolve",children:"Can resolve?"}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsxs)("div",{children:[(0,r.jsx)(n.p,{children:"There might be cases when you are more interested in whether a service is resolvable from the container's actual state rather than finding out whether it's registered."}),(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"CanResolve()"})," returns ",(0,r.jsx)(n.code,{children:"true"})," only when at least one of the following is true:"]}),(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"The requested type is registered in the current or one of the parent containers."}),"\n",(0,r.jsx)(n.li,{children:"The requested type is a closed generic type, and its open generic definition is registered."}),"\n",(0,r.jsxs)(n.li,{children:["The requested type is a wrapper (",(0,r.jsx)(n.code,{children:"IEnumerable<>"}),", ",(0,r.jsx)(n.code,{children:"Lazy<>"}),", ",(0,r.jsx)(n.code,{children:"Func<>"}),", ",(0,r.jsx)(n.code,{children:"KeyValuePair<,>"}),", ",(0,r.jsx)(n.code,{children:"ReadOnlyKeyValue<,>"}),", ",(0,r.jsx)(n.code,{children:"Metadata<,>"}),", ",(0,r.jsx)(n.code,{children:"ValueTuple<,>"}),", or ",(0,r.jsx)(n.code,{children:"Tuple<,>"}),"), and the underlying type is registered."]}),"\n",(0,r.jsxs)(n.li,{children:["The requested type is not registered, but it's resolvable, and ",(0,r.jsx)(n.a,{href:"/docs/configuration/container-configuration#unknown-type-resolution",children:"unknown type resolution"})," is enabled."]}),"\n"]})]}),(0,r.jsx)("div",{children:(0,r.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,r.jsx)(l.A,{value:"Generic API",label:"Generic API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"bool isIJobResolvable = container.CanResolve();\n"})})}),(0,r.jsx)(l.A,{value:"Runtime type API",label:"Runtime type API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"bool isIJobResolvable = container.CanResolve(typeof(IJob));\n"})})}),(0,r.jsx)(l.A,{value:"Named",label:"Named",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:'bool isIJobResolvable = container.CanResolve("DbBackup");\n'})})})]})})]}),"\n",(0,r.jsx)(n.h2,{id:"get-all-mappings",children:"Get all mappings"}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsx)("div",{children:(0,r.jsxs)(n.p,{children:["You can get all registrations in a key-value pair collection (where the key is the ",(0,r.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," and the value is the actual registration) by calling the ",(0,r.jsx)(n.code,{children:".GetRegistrationMappings()"})," method."]})}),(0,r.jsx)("div",{children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"IEnumerable> mappings = \n container.GetRegistrationMappings();\n"})})})]}),"\n",(0,r.jsx)(n.h2,{id:"registration-diagnostics",children:"Registration diagnostics"}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsxs)("div",{children:[(0,r.jsxs)(n.p,{children:["You can get a much more readable version of the registration mappings by calling the ",(0,r.jsx)(n.code,{children:".GetRegistrationDiagnostics()"})," method."]}),(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"RegistrationDiagnosticsInfo"})," has an overridden ",(0,r.jsx)(n.code,{children:".ToString()"})," method that returns the mapping details formatted in a human-readable form."]})]}),(0,r.jsx)("div",{children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:'container.Register("DbBackupJob");\ncontainer.Register(typeof(IEventHandler<>), typeof(EventHandler<>));\n\nIEnumerable diagnostics = \n container.GetRegistrationDiagnostics();\n\ndiagnostics.ForEach(Console.WriteLine);\n// output:\n// IJob => DbBackup, name: DbBackupJob\n// IEventHandler<> => EventHandler<>, name: null\n'})})})]})]})}function g(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(p,{...e})}):p(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>a});t(6540);var r=t(870);const s={tabItem:"tabItem_Ymn6"};var i=t(4848);function a(e){let{children:n,hidden:t,className:a}=e;return(0,i.jsx)("div",{role:"tabpanel",className:(0,r.A)(s.tabItem,a),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>I});var r=t(6540),s=t(870),i=t(3104),a=t(6347),l=t(205),o=t(7485),c=t(1682),d=t(9466);function u(e){return r.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,r.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:t}=e;return(0,r.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:t,attributes:r,default:s}}=e;return{value:n,label:t,attributes:r,default:s}}))}(t);return function(e){const n=(0,c.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function p(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function g(e){let{queryString:n=!1,groupId:t}=e;const s=(0,a.W6)(),i=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,o.aZ)(i),(0,r.useCallback)((e=>{if(!i)return;const n=new URLSearchParams(s.location.search);n.set(i,e),s.replace({...s.location,search:n.toString()})}),[i,s])]}function b(e){const{defaultValue:n,queryString:t=!1,groupId:s}=e,i=h(e),[a,o]=(0,r.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const r=t.find((e=>e.default))??t[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:n,tabValues:i}))),[c,u]=g({queryString:t,groupId:s}),[b,m]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[s,i]=(0,d.Dv)(t);return[s,(0,r.useCallback)((e=>{t&&i.set(e)}),[t,i])]}({groupId:s}),v=(()=>{const e=c??b;return p({value:e,tabValues:i})?e:null})();(0,l.A)((()=>{v&&o(v)}),[v]);return{selectedValue:a,selectValue:(0,r.useCallback)((e=>{if(!p({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);o(e),u(e),m(e)}),[u,m,i]),tabValues:i}}var m=t(2303);const v={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var x=t(4848);function f(e){let{className:n,block:t,selectedValue:r,selectValue:a,tabValues:l}=e;const o=[],{blockElementScrollPositionUntilNextRender:c}=(0,i.a_)(),d=e=>{const n=e.currentTarget,t=o.indexOf(n),s=l[t].value;s!==r&&(c(n),a(s))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const t=o.indexOf(e.currentTarget)+1;n=o[t]??o[0];break}case"ArrowLeft":{const t=o.indexOf(e.currentTarget)-1;n=o[t]??o[o.length-1];break}}n?.focus()};return(0,x.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":t},n),children:l.map((e=>{let{value:n,label:t,attributes:i}=e;return(0,x.jsx)("li",{role:"tab",tabIndex:r===n?0:-1,"aria-selected":r===n,ref:e=>o.push(e),onKeyDown:u,onClick:d,...i,className:(0,s.A)("tabs__item",v.tabItem,i?.className,{"tabs__item--active":r===n}),children:t??n},n)}))})}function j(e){let{lazy:n,children:t,selectedValue:s}=e;const i=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===s));return e?(0,r.cloneElement)(e,{className:"margin-top--md"}):null}return(0,x.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,r.cloneElement)(e,{key:n,hidden:e.props.value!==s})))})}function y(e){const n=b(e);return(0,x.jsxs)("div",{className:(0,s.A)("tabs-container",v.tabList),children:[(0,x.jsx)(f,{...e,...n}),(0,x.jsx)(j,{...e,...n})]})}function I(e){const n=(0,m.A)();return(0,x.jsx)(y,{...e,children:u(e.children)},String(n))}},7470:(e,n,t)=>{t.d(n,{A:()=>a});var r=t(6540);const s={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var i=t(4848);function a(e){let{children:n}=e,t=r.Children.toArray(n).filter((e=>e));return(0,i.jsxs)("div",{className:s.codeDescContainer,children:[(0,i.jsx)("div",{className:s.desc,children:t[0]}),(0,i.jsx)("div",{className:s.example,children:t[1]})]})}},8453:(e,n,t)=>{t.d(n,{R:()=>a,x:()=>l});var r=t(6540);const s={},i=r.createContext(s);function a(e){const n=r.useContext(i);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:a(e.components),r.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[494],{9:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>c,default:()=>g,frontMatter:()=>o,metadata:()=>d,toc:()=>h});var r=t(4848),s=t(8453),i=t(7470),a=t(1470),l=t(9365);const o={},c="Utilities",d={id:"diagnostics/utilities",title:"Utilities",description:"Is registered?",source:"@site/docs/diagnostics/utilities.md",sourceDirName:"diagnostics",slug:"/diagnostics/utilities",permalink:"/stashbox/docs/diagnostics/utilities",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/diagnostics/utilities.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Validation",permalink:"/stashbox/docs/diagnostics/validation"}},u={},h=[{value:"Is registered?",id:"is-registered",level:2},{value:"Named",id:"named",level:4},{value:"Can resolve?",id:"can-resolve",level:2},{value:"Get all mappings",id:"get-all-mappings",level:2},{value:"Registration diagnostics",id:"registration-diagnostics",level:2}];function p(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h1,{id:"utilities",children:"Utilities"}),"\n",(0,r.jsx)(n.h2,{id:"is-registered",children:"Is registered?"}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsxs)("div",{children:[(0,r.jsxs)(n.p,{children:["With the ",(0,r.jsx)(n.code,{children:"IsRegistered()"})," function, you can find out whether a service is registered into the container or not."]}),(0,r.jsxs)(n.p,{children:["It returns ",(0,r.jsx)(n.code,{children:"true"})," only when the container has a registration with the given type (and name). It only checks the actual container's registrations. For every cases, you should use the ",(0,r.jsx)(n.code,{children:"CanResolve()"})," method."]})]}),(0,r.jsx)("div",{children:(0,r.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,r.jsx)(l.A,{value:"Generic API",label:"Generic API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"bool isIJobRegistered = container.IsRegistered();\n"})})}),(0,r.jsx)(l.A,{value:"Runtime type API",label:"Runtime type API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"bool isIJobRegistered = container.IsRegistered(typeof(IJob));\n"})})}),(0,r.jsxs)(l.A,{value:"Named",label:"Named",children:[(0,r.jsx)(n.h4,{id:"named",children:(0,r.jsx)(n.strong,{children:"Named"})}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:'bool isIJobRegistered = container.IsRegistered("DbBackup");\n'})})]})]})})]}),"\n",(0,r.jsx)(n.h2,{id:"can-resolve",children:"Can resolve?"}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsxs)("div",{children:[(0,r.jsx)(n.p,{children:"There might be cases when you are more interested in whether a service is resolvable from the container's actual state rather than finding out whether it's registered."}),(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"CanResolve()"})," returns ",(0,r.jsx)(n.code,{children:"true"})," only when at least one of the following is true:"]}),(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"The requested type is registered in the current or one of the parent containers."}),"\n",(0,r.jsx)(n.li,{children:"The requested type is a closed generic type, and its open generic definition is registered."}),"\n",(0,r.jsxs)(n.li,{children:["The requested type is a wrapper (",(0,r.jsx)(n.code,{children:"IEnumerable<>"}),", ",(0,r.jsx)(n.code,{children:"Lazy<>"}),", ",(0,r.jsx)(n.code,{children:"Func<>"}),", ",(0,r.jsx)(n.code,{children:"KeyValuePair<,>"}),", ",(0,r.jsx)(n.code,{children:"ReadOnlyKeyValue<,>"}),", ",(0,r.jsx)(n.code,{children:"Metadata<,>"}),", ",(0,r.jsx)(n.code,{children:"ValueTuple<,>"}),", or ",(0,r.jsx)(n.code,{children:"Tuple<,>"}),"), and the underlying type is registered."]}),"\n",(0,r.jsxs)(n.li,{children:["The requested type is not registered, but it's resolvable, and ",(0,r.jsx)(n.a,{href:"/docs/configuration/container-configuration#unknown-type-resolution",children:"unknown type resolution"})," is enabled."]}),"\n"]})]}),(0,r.jsx)("div",{children:(0,r.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,r.jsx)(l.A,{value:"Generic API",label:"Generic API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"bool isIJobResolvable = container.CanResolve();\n"})})}),(0,r.jsx)(l.A,{value:"Runtime type API",label:"Runtime type API",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"bool isIJobResolvable = container.CanResolve(typeof(IJob));\n"})})}),(0,r.jsx)(l.A,{value:"Named",label:"Named",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:'bool isIJobResolvable = container.CanResolve("DbBackup");\n'})})})]})})]}),"\n",(0,r.jsx)(n.h2,{id:"get-all-mappings",children:"Get all mappings"}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsx)("div",{children:(0,r.jsxs)(n.p,{children:["You can get all registrations in a key-value pair collection (where the key is the ",(0,r.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," and the value is the actual registration) by calling the ",(0,r.jsx)(n.code,{children:".GetRegistrationMappings()"})," method."]})}),(0,r.jsx)("div",{children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:"IEnumerable> mappings = \n container.GetRegistrationMappings();\n"})})})]}),"\n",(0,r.jsx)(n.h2,{id:"registration-diagnostics",children:"Registration diagnostics"}),"\n",(0,r.jsxs)(i.A,{children:[(0,r.jsxs)("div",{children:[(0,r.jsxs)(n.p,{children:["You can get a much more readable version of the registration mappings by calling the ",(0,r.jsx)(n.code,{children:".GetRegistrationDiagnostics()"})," method."]}),(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"RegistrationDiagnosticsInfo"})," has an overridden ",(0,r.jsx)(n.code,{children:".ToString()"})," method that returns the mapping details formatted in a human-readable form."]})]}),(0,r.jsx)("div",{children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cs",children:'container.Register("DbBackupJob");\ncontainer.Register(typeof(IEventHandler<>), typeof(EventHandler<>));\n\nIEnumerable diagnostics = \n container.GetRegistrationDiagnostics();\n\ndiagnostics.ForEach(Console.WriteLine);\n// output:\n// IJob => DbBackup, name: DbBackupJob\n// IEventHandler<> => EventHandler<>, name: null\n'})})})]})]})}function g(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(p,{...e})}):p(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>a});t(6540);var r=t(870);const s={tabItem:"tabItem_Ymn6"};var i=t(4848);function a(e){let{children:n,hidden:t,className:a}=e;return(0,i.jsx)("div",{role:"tabpanel",className:(0,r.A)(s.tabItem,a),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>I});var r=t(6540),s=t(870),i=t(3104),a=t(6347),l=t(205),o=t(7485),c=t(1682),d=t(9466);function u(e){return r.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,r.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:t}=e;return(0,r.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:t,attributes:r,default:s}}=e;return{value:n,label:t,attributes:r,default:s}}))}(t);return function(e){const n=(0,c.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function p(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function g(e){let{queryString:n=!1,groupId:t}=e;const s=(0,a.W6)(),i=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,o.aZ)(i),(0,r.useCallback)((e=>{if(!i)return;const n=new URLSearchParams(s.location.search);n.set(i,e),s.replace({...s.location,search:n.toString()})}),[i,s])]}function b(e){const{defaultValue:n,queryString:t=!1,groupId:s}=e,i=h(e),[a,o]=(0,r.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const r=t.find((e=>e.default))??t[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:n,tabValues:i}))),[c,u]=g({queryString:t,groupId:s}),[b,m]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[s,i]=(0,d.Dv)(t);return[s,(0,r.useCallback)((e=>{t&&i.set(e)}),[t,i])]}({groupId:s}),v=(()=>{const e=c??b;return p({value:e,tabValues:i})?e:null})();(0,l.A)((()=>{v&&o(v)}),[v]);return{selectedValue:a,selectValue:(0,r.useCallback)((e=>{if(!p({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);o(e),u(e),m(e)}),[u,m,i]),tabValues:i}}var m=t(2303);const v={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var x=t(4848);function f(e){let{className:n,block:t,selectedValue:r,selectValue:a,tabValues:l}=e;const o=[],{blockElementScrollPositionUntilNextRender:c}=(0,i.a_)(),d=e=>{const n=e.currentTarget,t=o.indexOf(n),s=l[t].value;s!==r&&(c(n),a(s))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const t=o.indexOf(e.currentTarget)+1;n=o[t]??o[0];break}case"ArrowLeft":{const t=o.indexOf(e.currentTarget)-1;n=o[t]??o[o.length-1];break}}n?.focus()};return(0,x.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":t},n),children:l.map((e=>{let{value:n,label:t,attributes:i}=e;return(0,x.jsx)("li",{role:"tab",tabIndex:r===n?0:-1,"aria-selected":r===n,ref:e=>o.push(e),onKeyDown:u,onClick:d,...i,className:(0,s.A)("tabs__item",v.tabItem,i?.className,{"tabs__item--active":r===n}),children:t??n},n)}))})}function j(e){let{lazy:n,children:t,selectedValue:s}=e;const i=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===s));return e?(0,r.cloneElement)(e,{className:"margin-top--md"}):null}return(0,x.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,r.cloneElement)(e,{key:n,hidden:e.props.value!==s})))})}function y(e){const n=b(e);return(0,x.jsxs)("div",{className:(0,s.A)("tabs-container",v.tabList),children:[(0,x.jsx)(f,{...e,...n}),(0,x.jsx)(j,{...e,...n})]})}function I(e){const n=(0,m.A)();return(0,x.jsx)(y,{...e,children:u(e.children)},String(n))}},7470:(e,n,t)=>{t.d(n,{A:()=>a});var r=t(6540);const s={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var i=t(4848);function a(e){let{children:n}=e,t=r.Children.toArray(n).filter((e=>e));return(0,i.jsxs)("div",{className:s.codeDescContainer,children:[(0,i.jsx)("div",{className:s.desc,children:t[0]}),(0,i.jsx)("div",{className:s.example,children:t[1]})]})}},8453:(e,n,t)=>{t.d(n,{R:()=>a,x:()=>l});var r=t(6540);const s={},i=r.createContext(s);function a(e){const n=r.useContext(i);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:a(e.components),r.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/b43f639d.30eb1c65.js b/assets/js/b43f639d.e18adc97.js similarity index 99% rename from assets/js/b43f639d.30eb1c65.js rename to assets/js/b43f639d.e18adc97.js index 219efe94..c17a724d 100644 --- a/assets/js/b43f639d.30eb1c65.js +++ b/assets/js/b43f639d.e18adc97.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[384],{9743:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>d});var t=i(4848),s=i(8453),r=i(7470);i(1470),i(9365);const o={},a="Container configuration",l={id:"configuration/container-configuration",title:"Container configuration",description:"The container's constructor has an Action parameter used to configure its behavior.",source:"@site/docs/configuration/container-configuration.md",sourceDirName:"configuration",slug:"/configuration/container-configuration",permalink:"/stashbox/docs/configuration/container-configuration",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/configuration/container-configuration.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Registration configuration",permalink:"/stashbox/docs/configuration/registration-configuration"},next:{title:"Generics",permalink:"/stashbox/docs/advanced/generics"}},c={},d=[{value:"Default configuration",id:"default-configuration",level:2},{value:"Tracking disposable transients",id:"tracking-disposable-transients",level:2},{value:"Auto member-injection",id:"auto-member-injection",level:2},{value:"PropertiesWithPublicSetter",id:"propertieswithpublicsetter",level:3},{value:"PropertiesWithLimitedAccess",id:"propertieswithlimitedaccess",level:3},{value:"PrivateFields",id:"privatefields",level:3},{value:"Combined rules",id:"combined-rules",level:3},{value:"Member selection filter",id:"member-selection-filter",level:4},{value:"Required member injection",id:"required-member-injection",level:2},{value:"Constructor selection",id:"constructor-selection",level:2},{value:"PreferMostParameters",id:"prefermostparameters",level:3},{value:"PreferLeastParameters",id:"preferleastparameters",level:3},{value:"Registration behavior",id:"registration-behavior",level:2},{value:"SkipDuplications",id:"skipduplications",level:3},{value:"ThrowException",id:"throwexception",level:3},{value:"ReplaceExisting",id:"replaceexisting",level:3},{value:"PreserveDuplications",id:"preserveduplications",level:3},{value:"Default lifetime",id:"default-lifetime",level:2},{value:"Lifetime validation",id:"lifetime-validation",level:2},{value:"Generic variance",id:"generic-variance",level:2},{value:"Empty collection handling",id:"empty-collection-handling",level:2},{value:"Conventional resolution",id:"conventional-resolution",level:2},{value:"Using named service for un-named requests",id:"using-named-service-for-un-named-requests",level:2},{value:"Named service resolution",id:"named-service-resolution",level:2},{value:"WithUniversalName",id:"withuniversalname",level:3},{value:"WithAdditionalDependencyNameAttribute",id:"withadditionaldependencynameattribute",level:3},{value:"WithAdditionalDependencyAttribute",id:"withadditionaldependencyattribute",level:3},{value:"Default value injection",id:"default-value-injection",level:2},{value:"Unknown type resolution",id:"unknown-type-resolution",level:2},{value:"Custom compiler",id:"custom-compiler",level:2},{value:"Re-build singletons in child containers",id:"re-build-singletons-in-child-containers",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{id:"container-configuration",children:"Container configuration"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["The container's constructor has an ",(0,t.jsx)(n.code,{children:"Action"})," parameter used to configure its behavior."]}),(0,t.jsx)(n.p,{children:"The configuration API is fluent, which means you can chain the configuration methods after each other."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"var container = new StashboxContainer(options => options\n .WithDisposableTransientTracking()\n .WithConstructorSelectionRule(Rules.ConstructorSelection.PreferLeastParameters)\n .WithRegistrationBehavior(Rules.RegistrationBehavior.ThrowException));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Re-configuration"})," of the container is also supported by calling its ",(0,t.jsx)(n.code,{children:".Configure()"})," method."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"var container = new StashboxContainer();\ncontainer.Configure(options => options.WithDisposableTransientTracking());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"default-configuration",children:"Default configuration"}),"\n",(0,t.jsx)(n.p,{children:"These features are set by default:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"/docs/configuration/container-configuration#constructor-selection",children:"Constructor selection"}),": ",(0,t.jsx)(n.code,{children:"Rules.ConstructorSelection.PreferMostParameters"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"/docs/configuration/container-configuration#registration-behavior",children:"Registration behavior"}),": ",(0,t.jsx)(n.code,{children:"Rules.RegistrationBehavior.SkipDuplications"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"/docs/configuration/container-configuration#default-lifetime",children:"Default lifetime"}),": ",(0,t.jsx)(n.code,{children:"Lifetimes.Transient"})]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"tracking-disposable-transients",children:"Tracking disposable transients"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can enable or disable the tracking of disposable transient objects."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithDisposableTransientTracking());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"auto-member-injection",children:"Auto member-injection"}),"\n",(0,t.jsxs)(n.p,{children:["With this option, you can enable or disable the auto member-injection without ",(0,t.jsx)(n.a,{href:"/docs/guides/service-resolution#attributes",children:"attributes"}),"."]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"propertieswithpublicsetter",children:(0,t.jsx)(n.code,{children:"PropertiesWithPublicSetter"})}),(0,t.jsx)(n.p,{children:"With this flag, the container will perform auto-injection on properties with public setters."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAutoMemberInjection(\n Rules.AutoMemberInjectionRules.PropertiesWithPublicSetter));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"propertieswithlimitedaccess",children:(0,t.jsx)(n.code,{children:"PropertiesWithLimitedAccess"})}),(0,t.jsx)(n.p,{children:"With this flag, the container will perform auto-injection on properties even when they don't have a public setter."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAutoMemberInjection(\n Rules.AutoMemberInjectionRules.PropertiesWithLimitedAccess));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"privatefields",children:(0,t.jsx)(n.code,{children:"PrivateFields"})}),(0,t.jsx)(n.p,{children:"With this flag, the container will perform auto-injection on private fields too."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAutoMemberInjection(\n Rules.AutoMemberInjectionRules.PrivateFields));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"combined-rules",children:"Combined rules"}),(0,t.jsx)(n.p,{children:"You can also combine these flags with bitwise logical operators to get a merged ruleset."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAutoMemberInjection(Rules.AutoMemberInjectionRules.PrivateFields | \n Rules.AutoMemberInjectionRules.PropertiesWithPublicSetter));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h4,{id:"member-selection-filter",children:"Member selection filter"}),(0,t.jsx)(n.p,{children:"You can pass your own member selection logic to control which members should be auto injected."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAutoMemberInjection(\n filter: member => member.Type != typeof(ILogger)));\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"required-member-injection",children:"Required member injection"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["With this option, you can enable or disable the auto injection of members defined with C# 11's ",(0,t.jsx)(n.a,{href:"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required",children:(0,t.jsx)(n.code,{children:"required"})})," keyword."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithRequiredMemberInjection(enabled: false));\n"})})})]}),"\n",(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["The required member injection option is ",(0,t.jsx)(n.strong,{children:"enabled"})," by default."]})}),"\n",(0,t.jsx)(n.h2,{id:"constructor-selection",children:"Constructor selection"}),"\n",(0,t.jsx)(n.p,{children:"With this option, you can set the constructor selection rule used to determine which constructor the container should use for instantiation."}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"prefermostparameters",children:(0,t.jsx)(n.code,{children:"PreferMostParameters"})}),(0,t.jsx)(n.p,{children:"It prefers the constructor which has the most extended parameter list."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithConstructorSelectionRule(\n Rules.ConstructorSelection.PreferMostParameters));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"preferleastparameters",children:(0,t.jsx)(n.code,{children:"PreferLeastParameters"})}),(0,t.jsx)(n.p,{children:"It prefers the constructor which has the shortest parameter list."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithConstructorSelectionRule(\n Rules.ConstructorSelection.PreferLeastParameters));\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"registration-behavior",children:"Registration behavior"}),"\n",(0,t.jsx)(n.p,{children:"With this option, you can set the actual behavior used when a new service is registered into the container. These options do not affect named registrations."}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"skipduplications",children:(0,t.jsx)(n.code,{children:"SkipDuplications"})}),(0,t.jsxs)(n.p,{children:["The container will skip new registrations when the given ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," is already registered."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithRegistrationBehavior(\n Rules.RegistrationBehavior.SkipDuplications));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"throwexception",children:(0,t.jsx)(n.code,{children:"ThrowException"})}),(0,t.jsxs)(n.p,{children:["The container throws an ",(0,t.jsx)(n.a,{href:"/docs/diagnostics/validation#servicealreadyregisteredexception",children:"exception"})," when the given ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," is already registered."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithRegistrationBehavior(\n Rules.RegistrationBehavior.ThrowException));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"replaceexisting",children:(0,t.jsx)(n.code,{children:"ReplaceExisting"})}),(0,t.jsxs)(n.p,{children:["The container will replace the already ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"registered service"})," with the given one when they have the same ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithRegistrationBehavior(\n Rules.RegistrationBehavior.ReplaceExisting));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"preserveduplications",children:(0,t.jsx)(n.code,{children:"PreserveDuplications"})}),(0,t.jsxs)(n.p,{children:["The container will keep registering the new services with the same ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithRegistrationBehavior(\n Rules.RegistrationBehavior.PreserveDuplications));\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"default-lifetime",children:"Default lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can set the default lifetime used when a service doesn't have a configured one."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithDefaultLifetime(Lifetimes.Scoped));\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"lifetime-validation",children:"Lifetime validation"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["With this option, you can enable or disable the life-span and ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#root-scope",children:"root scope"})," resolution ",(0,t.jsx)(n.a,{href:"/docs/diagnostics/validation#lifetime-validation",children:"validation"})," on the dependency tree."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithLifetimeValidation());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"generic-variance",children:"Generic variance"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["With this option, you can enable or disable the check for ",(0,t.jsx)(n.a,{href:"/docs/advanced/generics#variance",children:"generic covariance and contravariance"})," during the resolution of generic type collections."]}),(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.em,{children:"This option is enabled by default"}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithVariantGenericTypes());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"empty-collection-handling",children:"Empty collection handling"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["With this option, you can enable or disable the throwing of a ",(0,t.jsx)(n.code,{children:"ResolutionFailedException"})," when no services are found for a collection resolution request. When this feature is disabled ",(0,t.jsx)(n.em,{children:"(default)"}),", the container returns an empty array for those request."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithExceptionOverEmptyCollection());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"conventional-resolution",children:"Conventional resolution"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["With this option, you can enable or disable conventional resolution, which means the container treats the constructor/method parameter or member names as dependency names used by ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"}),"."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .TreatParameterAndMemberNameAsDependencyName());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"using-named-service-for-un-named-requests",children:"Using named service for un-named requests"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can enable or disable the selection of named registrations when the resolution request is un-named but with the same type."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithNamedDependencyResolutionForUnNamedRequests());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"named-service-resolution",children:"Named service resolution"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"withuniversalname",children:(0,t.jsx)(n.code,{children:"WithUniversalName"})}),(0,t.jsx)(n.p,{children:"Sets the universal name that represents a special name which allows named resolution work for any given name."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'new StashboxContainer(options => options\n .WithUniversalName("Any"));\n'})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"withadditionaldependencynameattribute",children:(0,t.jsx)(n.code,{children:"WithAdditionalDependencyNameAttribute"})}),(0,t.jsxs)(n.p,{children:["Adds an attribute type that is considered a dependency name indicator just like the ",(0,t.jsxs)(n.a,{href:"/docs/guides/service-resolution#attributes",children:[(0,t.jsx)(n.code,{children:"DependencyName"})," attribute"]}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAdditionalDependencyNameAttribute());\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"withadditionaldependencyattribute",children:(0,t.jsx)(n.code,{children:"WithAdditionalDependencyAttribute"})}),(0,t.jsxs)(n.p,{children:["Adds an attribute type that is considered a dependency indicator just like the ",(0,t.jsxs)(n.a,{href:"/docs/guides/service-resolution#attributes",children:[(0,t.jsx)(n.code,{children:"Dependency"})," attribute"]}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAdditionalDependencyAttribute());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"default-value-injection",children:"Default value injection"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can enable or disable the default value injection."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithDefaultValueInjection());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"unknown-type-resolution",children:"Unknown type resolution"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can enable or disable the resolution of unregistered types. You can also use a configurator delegate to configure the registrations the container will create from the unknown types."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithUnknownTypeResolution(config => config.AsImplementedTypes()));\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"custom-compiler",children:"Custom compiler"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can set an external expression tree compiler. It can be useful on platforms where the IL generator modules are not available; therefore, the expression compiler in Stashbox couldn't work."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithExpressionCompiler(\n Rules.ExpressionCompilers.MicrosoftExpressionCompiler));\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"re-build-singletons-in-child-containers",children:"Re-build singletons in child containers"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can enable or disable the re-building of singletons in child containers. It allows the child containers to override singleton dependencies in the parent."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithReBuildSingletonsInChildContainer());\n"})})})]}),"\n",(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsx)(n.p,{children:"This feature is not affecting the already built singleton instances in the parent."})})]})}function u(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}},9365:(e,n,i)=>{i.d(n,{A:()=>o});i(6540);var t=i(870);const s={tabItem:"tabItem_Ymn6"};var r=i(4848);function o(e){let{children:n,hidden:i,className:o}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,t.A)(s.tabItem,o),hidden:i,children:n})}},1470:(e,n,i)=>{i.d(n,{A:()=>w});var t=i(6540),s=i(870),r=i(3104),o=i(6347),a=i(205),l=i(7485),c=i(1682),d=i(9466);function h(e){return t.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,t.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function u(e){const{values:n,children:i}=e;return(0,t.useMemo)((()=>{const e=n??function(e){return h(e).map((e=>{let{props:{value:n,label:i,attributes:t,default:s}}=e;return{value:n,label:i,attributes:t,default:s}}))}(i);return function(e){const n=(0,c.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,i])}function p(e){let{value:n,tabValues:i}=e;return i.some((e=>e.value===n))}function x(e){let{queryString:n=!1,groupId:i}=e;const s=(0,o.W6)(),r=function(e){let{queryString:n=!1,groupId:i}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!i)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return i??null}({queryString:n,groupId:i});return[(0,l.aZ)(r),(0,t.useCallback)((e=>{if(!r)return;const n=new URLSearchParams(s.location.search);n.set(r,e),s.replace({...s.location,search:n.toString()})}),[r,s])]}function j(e){const{defaultValue:n,queryString:i=!1,groupId:s}=e,r=u(e),[o,l]=(0,t.useState)((()=>function(e){let{defaultValue:n,tabValues:i}=e;if(0===i.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:i}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${i.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const t=i.find((e=>e.default))??i[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:r}))),[c,h]=x({queryString:i,groupId:s}),[j,m]=function(e){let{groupId:n}=e;const i=function(e){return e?`docusaurus.tab.${e}`:null}(n),[s,r]=(0,d.Dv)(i);return[s,(0,t.useCallback)((e=>{i&&r.set(e)}),[i,r])]}({groupId:s}),v=(()=>{const e=c??j;return p({value:e,tabValues:r})?e:null})();(0,a.A)((()=>{v&&l(v)}),[v]);return{selectedValue:o,selectValue:(0,t.useCallback)((e=>{if(!p({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);l(e),h(e),m(e)}),[h,m,r]),tabValues:r}}var m=i(2303);const v={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var g=i(4848);function f(e){let{className:n,block:i,selectedValue:t,selectValue:o,tabValues:a}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,r.a_)(),d=e=>{const n=e.currentTarget,i=l.indexOf(n),s=a[i].value;s!==t&&(c(n),o(s))},h=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const i=l.indexOf(e.currentTarget)+1;n=l[i]??l[0];break}case"ArrowLeft":{const i=l.indexOf(e.currentTarget)-1;n=l[i]??l[l.length-1];break}}n?.focus()};return(0,g.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":i},n),children:a.map((e=>{let{value:n,label:i,attributes:r}=e;return(0,g.jsx)("li",{role:"tab",tabIndex:t===n?0:-1,"aria-selected":t===n,ref:e=>l.push(e),onKeyDown:h,onClick:d,...r,className:(0,s.A)("tabs__item",v.tabItem,r?.className,{"tabs__item--active":t===n}),children:i??n},n)}))})}function b(e){let{lazy:n,children:i,selectedValue:s}=e;const r=(Array.isArray(i)?i:[i]).filter(Boolean);if(n){const e=r.find((e=>e.props.value===s));return e?(0,t.cloneElement)(e,{className:"margin-top--md"}):null}return(0,g.jsx)("div",{className:"margin-top--md",children:r.map(((e,n)=>(0,t.cloneElement)(e,{key:n,hidden:e.props.value!==s})))})}function y(e){const n=j(e);return(0,g.jsxs)("div",{className:(0,s.A)("tabs-container",v.tabList),children:[(0,g.jsx)(f,{...e,...n}),(0,g.jsx)(b,{...e,...n})]})}function w(e){const n=(0,m.A)();return(0,g.jsx)(y,{...e,children:h(e.children)},String(n))}},7470:(e,n,i)=>{i.d(n,{A:()=>o});var t=i(6540);const s={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var r=i(4848);function o(e){let{children:n}=e,i=t.Children.toArray(n).filter((e=>e));return(0,r.jsxs)("div",{className:s.codeDescContainer,children:[(0,r.jsx)("div",{className:s.desc,children:i[0]}),(0,r.jsx)("div",{className:s.example,children:i[1]})]})}},8453:(e,n,i)=>{i.d(n,{R:()=>o,x:()=>a});var t=i(6540);const s={},r=t.createContext(s);function o(e){const n=t.useContext(r);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),t.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[384],{9743:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>d});var t=i(4848),s=i(8453),r=i(7470);i(1470),i(9365);const o={},a="Container configuration",l={id:"configuration/container-configuration",title:"Container configuration",description:"The container's constructor has an Action parameter used to configure its behavior.",source:"@site/docs/configuration/container-configuration.md",sourceDirName:"configuration",slug:"/configuration/container-configuration",permalink:"/stashbox/docs/configuration/container-configuration",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/configuration/container-configuration.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Registration configuration",permalink:"/stashbox/docs/configuration/registration-configuration"},next:{title:"Generics",permalink:"/stashbox/docs/advanced/generics"}},c={},d=[{value:"Default configuration",id:"default-configuration",level:2},{value:"Tracking disposable transients",id:"tracking-disposable-transients",level:2},{value:"Auto member-injection",id:"auto-member-injection",level:2},{value:"PropertiesWithPublicSetter",id:"propertieswithpublicsetter",level:3},{value:"PropertiesWithLimitedAccess",id:"propertieswithlimitedaccess",level:3},{value:"PrivateFields",id:"privatefields",level:3},{value:"Combined rules",id:"combined-rules",level:3},{value:"Member selection filter",id:"member-selection-filter",level:4},{value:"Required member injection",id:"required-member-injection",level:2},{value:"Constructor selection",id:"constructor-selection",level:2},{value:"PreferMostParameters",id:"prefermostparameters",level:3},{value:"PreferLeastParameters",id:"preferleastparameters",level:3},{value:"Registration behavior",id:"registration-behavior",level:2},{value:"SkipDuplications",id:"skipduplications",level:3},{value:"ThrowException",id:"throwexception",level:3},{value:"ReplaceExisting",id:"replaceexisting",level:3},{value:"PreserveDuplications",id:"preserveduplications",level:3},{value:"Default lifetime",id:"default-lifetime",level:2},{value:"Lifetime validation",id:"lifetime-validation",level:2},{value:"Generic variance",id:"generic-variance",level:2},{value:"Empty collection handling",id:"empty-collection-handling",level:2},{value:"Conventional resolution",id:"conventional-resolution",level:2},{value:"Using named service for un-named requests",id:"using-named-service-for-un-named-requests",level:2},{value:"Named service resolution",id:"named-service-resolution",level:2},{value:"WithUniversalName",id:"withuniversalname",level:3},{value:"WithAdditionalDependencyNameAttribute",id:"withadditionaldependencynameattribute",level:3},{value:"WithAdditionalDependencyAttribute",id:"withadditionaldependencyattribute",level:3},{value:"Default value injection",id:"default-value-injection",level:2},{value:"Unknown type resolution",id:"unknown-type-resolution",level:2},{value:"Custom compiler",id:"custom-compiler",level:2},{value:"Re-build singletons in child containers",id:"re-build-singletons-in-child-containers",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{id:"container-configuration",children:"Container configuration"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["The container's constructor has an ",(0,t.jsx)(n.code,{children:"Action"})," parameter used to configure its behavior."]}),(0,t.jsx)(n.p,{children:"The configuration API is fluent, which means you can chain the configuration methods after each other."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"var container = new StashboxContainer(options => options\n .WithDisposableTransientTracking()\n .WithConstructorSelectionRule(Rules.ConstructorSelection.PreferLeastParameters)\n .WithRegistrationBehavior(Rules.RegistrationBehavior.ThrowException));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Re-configuration"})," of the container is also supported by calling its ",(0,t.jsx)(n.code,{children:".Configure()"})," method."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"var container = new StashboxContainer();\ncontainer.Configure(options => options.WithDisposableTransientTracking());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"default-configuration",children:"Default configuration"}),"\n",(0,t.jsx)(n.p,{children:"These features are set by default:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"/docs/configuration/container-configuration#constructor-selection",children:"Constructor selection"}),": ",(0,t.jsx)(n.code,{children:"Rules.ConstructorSelection.PreferMostParameters"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"/docs/configuration/container-configuration#registration-behavior",children:"Registration behavior"}),": ",(0,t.jsx)(n.code,{children:"Rules.RegistrationBehavior.SkipDuplications"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"/docs/configuration/container-configuration#default-lifetime",children:"Default lifetime"}),": ",(0,t.jsx)(n.code,{children:"Lifetimes.Transient"})]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"tracking-disposable-transients",children:"Tracking disposable transients"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can enable or disable the tracking of disposable transient objects."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithDisposableTransientTracking());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"auto-member-injection",children:"Auto member-injection"}),"\n",(0,t.jsxs)(n.p,{children:["With this option, you can enable or disable the auto member-injection without ",(0,t.jsx)(n.a,{href:"/docs/guides/service-resolution#attributes",children:"attributes"}),"."]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"propertieswithpublicsetter",children:(0,t.jsx)(n.code,{children:"PropertiesWithPublicSetter"})}),(0,t.jsx)(n.p,{children:"With this flag, the container will perform auto-injection on properties with public setters."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAutoMemberInjection(\n Rules.AutoMemberInjectionRules.PropertiesWithPublicSetter));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"propertieswithlimitedaccess",children:(0,t.jsx)(n.code,{children:"PropertiesWithLimitedAccess"})}),(0,t.jsx)(n.p,{children:"With this flag, the container will perform auto-injection on properties even when they don't have a public setter."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAutoMemberInjection(\n Rules.AutoMemberInjectionRules.PropertiesWithLimitedAccess));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"privatefields",children:(0,t.jsx)(n.code,{children:"PrivateFields"})}),(0,t.jsx)(n.p,{children:"With this flag, the container will perform auto-injection on private fields too."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAutoMemberInjection(\n Rules.AutoMemberInjectionRules.PrivateFields));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"combined-rules",children:"Combined rules"}),(0,t.jsx)(n.p,{children:"You can also combine these flags with bitwise logical operators to get a merged ruleset."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAutoMemberInjection(Rules.AutoMemberInjectionRules.PrivateFields | \n Rules.AutoMemberInjectionRules.PropertiesWithPublicSetter));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h4,{id:"member-selection-filter",children:"Member selection filter"}),(0,t.jsx)(n.p,{children:"You can pass your own member selection logic to control which members should be auto injected."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAutoMemberInjection(\n filter: member => member.Type != typeof(ILogger)));\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"required-member-injection",children:"Required member injection"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["With this option, you can enable or disable the auto injection of members defined with C# 11's ",(0,t.jsx)(n.a,{href:"https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required",children:(0,t.jsx)(n.code,{children:"required"})})," keyword."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithRequiredMemberInjection(enabled: false));\n"})})})]}),"\n",(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["The required member injection option is ",(0,t.jsx)(n.strong,{children:"enabled"})," by default."]})}),"\n",(0,t.jsx)(n.h2,{id:"constructor-selection",children:"Constructor selection"}),"\n",(0,t.jsx)(n.p,{children:"With this option, you can set the constructor selection rule used to determine which constructor the container should use for instantiation."}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"prefermostparameters",children:(0,t.jsx)(n.code,{children:"PreferMostParameters"})}),(0,t.jsx)(n.p,{children:"It prefers the constructor which has the most extended parameter list."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithConstructorSelectionRule(\n Rules.ConstructorSelection.PreferMostParameters));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"preferleastparameters",children:(0,t.jsx)(n.code,{children:"PreferLeastParameters"})}),(0,t.jsx)(n.p,{children:"It prefers the constructor which has the shortest parameter list."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithConstructorSelectionRule(\n Rules.ConstructorSelection.PreferLeastParameters));\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"registration-behavior",children:"Registration behavior"}),"\n",(0,t.jsx)(n.p,{children:"With this option, you can set the actual behavior used when a new service is registered into the container. These options do not affect named registrations."}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"skipduplications",children:(0,t.jsx)(n.code,{children:"SkipDuplications"})}),(0,t.jsxs)(n.p,{children:["The container will skip new registrations when the given ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," is already registered."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithRegistrationBehavior(\n Rules.RegistrationBehavior.SkipDuplications));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"throwexception",children:(0,t.jsx)(n.code,{children:"ThrowException"})}),(0,t.jsxs)(n.p,{children:["The container throws an ",(0,t.jsx)(n.a,{href:"/docs/diagnostics/validation#servicealreadyregisteredexception",children:"exception"})," when the given ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," is already registered."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithRegistrationBehavior(\n Rules.RegistrationBehavior.ThrowException));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"replaceexisting",children:(0,t.jsx)(n.code,{children:"ReplaceExisting"})}),(0,t.jsxs)(n.p,{children:["The container will replace the already ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-registration--registered-service",children:"registered service"})," with the given one when they have the same ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithRegistrationBehavior(\n Rules.RegistrationBehavior.ReplaceExisting));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"preserveduplications",children:(0,t.jsx)(n.code,{children:"PreserveDuplications"})}),(0,t.jsxs)(n.p,{children:["The container will keep registering the new services with the same ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithRegistrationBehavior(\n Rules.RegistrationBehavior.PreserveDuplications));\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"default-lifetime",children:"Default lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can set the default lifetime used when a service doesn't have a configured one."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithDefaultLifetime(Lifetimes.Scoped));\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"lifetime-validation",children:"Lifetime validation"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["With this option, you can enable or disable the life-span and ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#root-scope",children:"root scope"})," resolution ",(0,t.jsx)(n.a,{href:"/docs/diagnostics/validation#lifetime-validation",children:"validation"})," on the dependency tree."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithLifetimeValidation());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"generic-variance",children:"Generic variance"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["With this option, you can enable or disable the check for ",(0,t.jsx)(n.a,{href:"/docs/advanced/generics#variance",children:"generic covariance and contravariance"})," during the resolution of generic type collections."]}),(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.em,{children:"This option is enabled by default"}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithVariantGenericTypes());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"empty-collection-handling",children:"Empty collection handling"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["With this option, you can enable or disable the throwing of a ",(0,t.jsx)(n.code,{children:"ResolutionFailedException"})," when no services are found for a collection resolution request. When this feature is disabled ",(0,t.jsx)(n.em,{children:"(default)"}),", the container returns an empty array for those request."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithExceptionOverEmptyCollection());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"conventional-resolution",children:"Conventional resolution"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["With this option, you can enable or disable conventional resolution, which means the container treats the constructor/method parameter or member names as dependency names used by ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#named-resolution",children:"named resolution"}),"."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .TreatParameterAndMemberNameAsDependencyName());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"using-named-service-for-un-named-requests",children:"Using named service for un-named requests"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can enable or disable the selection of named registrations when the resolution request is un-named but with the same type."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithNamedDependencyResolutionForUnNamedRequests());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"named-service-resolution",children:"Named service resolution"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"withuniversalname",children:(0,t.jsx)(n.code,{children:"WithUniversalName"})}),(0,t.jsx)(n.p,{children:"Sets the universal name that represents a special name which allows named resolution work for any given name."})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'new StashboxContainer(options => options\n .WithUniversalName("Any"));\n'})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"withadditionaldependencynameattribute",children:(0,t.jsx)(n.code,{children:"WithAdditionalDependencyNameAttribute"})}),(0,t.jsxs)(n.p,{children:["Adds an attribute type that is considered a dependency name indicator just like the ",(0,t.jsxs)(n.a,{href:"/docs/guides/service-resolution#attributes",children:[(0,t.jsx)(n.code,{children:"DependencyName"})," attribute"]}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAdditionalDependencyNameAttribute());\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.h3,{id:"withadditionaldependencyattribute",children:(0,t.jsx)(n.code,{children:"WithAdditionalDependencyAttribute"})}),(0,t.jsxs)(n.p,{children:["Adds an attribute type that is considered a dependency indicator just like the ",(0,t.jsxs)(n.a,{href:"/docs/guides/service-resolution#attributes",children:[(0,t.jsx)(n.code,{children:"Dependency"})," attribute"]}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithAdditionalDependencyAttribute());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"default-value-injection",children:"Default value injection"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can enable or disable the default value injection."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithDefaultValueInjection());\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"unknown-type-resolution",children:"Unknown type resolution"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can enable or disable the resolution of unregistered types. You can also use a configurator delegate to configure the registrations the container will create from the unknown types."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithUnknownTypeResolution(config => config.AsImplementedTypes()));\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"custom-compiler",children:"Custom compiler"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can set an external expression tree compiler. It can be useful on platforms where the IL generator modules are not available; therefore, the expression compiler in Stashbox couldn't work."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithExpressionCompiler(\n Rules.ExpressionCompilers.MicrosoftExpressionCompiler));\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"re-build-singletons-in-child-containers",children:"Re-build singletons in child containers"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"With this option, you can enable or disable the re-building of singletons in child containers. It allows the child containers to override singleton dependencies in the parent."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"new StashboxContainer(options => options\n .WithReBuildSingletonsInChildContainer());\n"})})})]}),"\n",(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsx)(n.p,{children:"This feature is not affecting the already built singleton instances in the parent."})})]})}function u(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}},9365:(e,n,i)=>{i.d(n,{A:()=>o});i(6540);var t=i(870);const s={tabItem:"tabItem_Ymn6"};var r=i(4848);function o(e){let{children:n,hidden:i,className:o}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,t.A)(s.tabItem,o),hidden:i,children:n})}},1470:(e,n,i)=>{i.d(n,{A:()=>w});var t=i(6540),s=i(870),r=i(3104),o=i(6347),a=i(205),l=i(7485),c=i(1682),d=i(9466);function h(e){return t.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,t.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function u(e){const{values:n,children:i}=e;return(0,t.useMemo)((()=>{const e=n??function(e){return h(e).map((e=>{let{props:{value:n,label:i,attributes:t,default:s}}=e;return{value:n,label:i,attributes:t,default:s}}))}(i);return function(e){const n=(0,c.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,i])}function p(e){let{value:n,tabValues:i}=e;return i.some((e=>e.value===n))}function x(e){let{queryString:n=!1,groupId:i}=e;const s=(0,o.W6)(),r=function(e){let{queryString:n=!1,groupId:i}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!i)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return i??null}({queryString:n,groupId:i});return[(0,l.aZ)(r),(0,t.useCallback)((e=>{if(!r)return;const n=new URLSearchParams(s.location.search);n.set(r,e),s.replace({...s.location,search:n.toString()})}),[r,s])]}function j(e){const{defaultValue:n,queryString:i=!1,groupId:s}=e,r=u(e),[o,l]=(0,t.useState)((()=>function(e){let{defaultValue:n,tabValues:i}=e;if(0===i.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:i}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${i.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const t=i.find((e=>e.default))??i[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:r}))),[c,h]=x({queryString:i,groupId:s}),[j,m]=function(e){let{groupId:n}=e;const i=function(e){return e?`docusaurus.tab.${e}`:null}(n),[s,r]=(0,d.Dv)(i);return[s,(0,t.useCallback)((e=>{i&&r.set(e)}),[i,r])]}({groupId:s}),v=(()=>{const e=c??j;return p({value:e,tabValues:r})?e:null})();(0,a.A)((()=>{v&&l(v)}),[v]);return{selectedValue:o,selectValue:(0,t.useCallback)((e=>{if(!p({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);l(e),h(e),m(e)}),[h,m,r]),tabValues:r}}var m=i(2303);const v={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var g=i(4848);function f(e){let{className:n,block:i,selectedValue:t,selectValue:o,tabValues:a}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,r.a_)(),d=e=>{const n=e.currentTarget,i=l.indexOf(n),s=a[i].value;s!==t&&(c(n),o(s))},h=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const i=l.indexOf(e.currentTarget)+1;n=l[i]??l[0];break}case"ArrowLeft":{const i=l.indexOf(e.currentTarget)-1;n=l[i]??l[l.length-1];break}}n?.focus()};return(0,g.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":i},n),children:a.map((e=>{let{value:n,label:i,attributes:r}=e;return(0,g.jsx)("li",{role:"tab",tabIndex:t===n?0:-1,"aria-selected":t===n,ref:e=>l.push(e),onKeyDown:h,onClick:d,...r,className:(0,s.A)("tabs__item",v.tabItem,r?.className,{"tabs__item--active":t===n}),children:i??n},n)}))})}function b(e){let{lazy:n,children:i,selectedValue:s}=e;const r=(Array.isArray(i)?i:[i]).filter(Boolean);if(n){const e=r.find((e=>e.props.value===s));return e?(0,t.cloneElement)(e,{className:"margin-top--md"}):null}return(0,g.jsx)("div",{className:"margin-top--md",children:r.map(((e,n)=>(0,t.cloneElement)(e,{key:n,hidden:e.props.value!==s})))})}function y(e){const n=j(e);return(0,g.jsxs)("div",{className:(0,s.A)("tabs-container",v.tabList),children:[(0,g.jsx)(f,{...e,...n}),(0,g.jsx)(b,{...e,...n})]})}function w(e){const n=(0,m.A)();return(0,g.jsx)(y,{...e,children:h(e.children)},String(n))}},7470:(e,n,i)=>{i.d(n,{A:()=>o});var t=i(6540);const s={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var r=i(4848);function o(e){let{children:n}=e,i=t.Children.toArray(n).filter((e=>e));return(0,r.jsxs)("div",{className:s.codeDescContainer,children:[(0,r.jsx)("div",{className:s.desc,children:i[0]}),(0,r.jsx)("div",{className:s.example,children:i[1]})]})}},8453:(e,n,i)=>{i.d(n,{R:()=>o,x:()=>a});var t=i(6540);const s={},r=t.createContext(s);function o(e){const n=t.useContext(r);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),t.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/b53e7bc5.56104c16.js b/assets/js/b53e7bc5.2ffd6d83.js similarity index 99% rename from assets/js/b53e7bc5.56104c16.js rename to assets/js/b53e7bc5.2ffd6d83.js index 659fc167..335a23d2 100644 --- a/assets/js/b53e7bc5.56104c16.js +++ b/assets/js/b53e7bc5.2ffd6d83.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[371],{9721:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>l,default:()=>p,frontMatter:()=>s,metadata:()=>c,toc:()=>d});var a=t(4848),r=t(8453),i=t(1470),o=t(9365);const s={},l="Special resolution cases",c={id:"advanced/special-resolution-cases",title:"Special resolution cases",description:"Unknown type resolution",source:"@site/docs/advanced/special-resolution-cases.md",sourceDirName:"advanced",slug:"/advanced/special-resolution-cases",permalink:"/stashbox/docs/advanced/special-resolution-cases",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/advanced/special-resolution-cases.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Child containers",permalink:"/stashbox/docs/advanced/child-containers"},next:{title:"Validation",permalink:"/stashbox/docs/diagnostics/validation"}},u={},d=[{value:"Unknown type resolution",id:"unknown-type-resolution",level:2},{value:"Default value injection",id:"default-value-injection",level:2},{value:"Optional value injection",id:"optional-value-injection",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",p:"p",pre:"pre",...(0,r.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.h1,{id:"special-resolution-cases",children:"Special resolution cases"}),"\n",(0,a.jsx)(n.h2,{id:"unknown-type-resolution",children:"Unknown type resolution"}),"\n",(0,a.jsxs)(n.p,{children:["When this ",(0,a.jsx)(n.a,{href:"/docs/configuration/container-configuration#unknown-type-resolution",children:"feature"})," is enabled, the container will try to resolve unregistered types by registering them using a pre-defined configuration delegate."]}),"\n",(0,a.jsxs)(i.A,{children:[(0,a.jsxs)(o.A,{value:"Default",label:"Default",children:[(0,a.jsxs)(n.p,{children:["Without a registration configuration, the container can resolve only non-interface and non-abstract unknown types. In the following example,\nthe container creates an implicit registration for ",(0,a.jsx)(n.code,{children:"Dependency"})," and injects its instance into ",(0,a.jsx)(n.code,{children:"Service"}),"."]}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cs",children:"class Dependency { }\n\nclass Service \n{\n public Service(Dependency dependency)\n { } \n}\n\nvar container = new StashboxContainer(config => config\n .WithUnknownTypeResolution());\n\ncontainer.Register();\n\nvar service = container.Resolve();\n"})})]}),(0,a.jsxs)(o.A,{value:"With registration configuration",label:"With registration configuration",children:[(0,a.jsxs)(n.p,{children:["With a registration configuration, you can control how an unknown type's individual registration should behave. You can also react to a service resolution request. In the following example, we tell the container that if it finds an unregistered ",(0,a.jsx)(n.code,{children:"IDependency"})," for the first time, that should be mapped to ",(0,a.jsx)(n.code,{children:"Dependency"})," and have a singleton lifetime. Next time, when the container comes across this service, it will use the registration created at the first request."]}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cs",children:"interface IDependency { }\n\nclass Dependency : IDependency { }\n\nclass Service \n{\n public Service(IDependency dependency)\n { } \n}\n\nvar container = new StashboxContainer(config => config\n .WithUnknownTypeResolution(options => \n {\n if(options.ServiceType == typeof(IDependency))\n {\n options.SetImplementationType(typeof(Dependency))\n .WithLifetime(Lifetimes.Singleton);\n }\n }));\n\ncontainer.Register();\n\nvar service = container.Resolve();\n"})})]})]}),"\n",(0,a.jsx)(n.h2,{id:"default-value-injection",children:"Default value injection"}),"\n",(0,a.jsxs)(n.p,{children:["When this ",(0,a.jsx)(n.a,{href:"/docs/configuration/container-configuration#default-value-injection",children:"feature"})," is enabled, the container will resolve unknown primitive dependencies with their default value."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cs",children:"class Person \n{\n public Person(string name, int age) { }\n}\n\nvar container = new StashboxContainer(config => config\n .WithDefaultValueInjection());\n// the name parameter will be null and the age will be 0.\nvar person = container.Resolve();\n"})}),"\n",(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsxs)(n.p,{children:["Unknown reference types are resolved to ",(0,a.jsx)(n.code,{children:"null"})," only in properties and fields."]})}),"\n",(0,a.jsx)(n.h2,{id:"optional-value-injection",children:"Optional value injection"}),"\n",(0,a.jsx)(n.p,{children:"Stashbox respects the optional value of each constructor and method argument."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cs",children:"class Person \n{\n public Person(string name = null, int age = 54, IContact contact = null) { }\n}\n\n// the name will be null \n// the age will be 54.\n// the contact will be null.\nvar person = container.Resolve();\n"})})]})}function p(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(h,{...e})}):h(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>o});t(6540);var a=t(870);const r={tabItem:"tabItem_Ymn6"};var i=t(4848);function o(e){let{children:n,hidden:t,className:o}=e;return(0,i.jsx)("div",{role:"tabpanel",className:(0,a.A)(r.tabItem,o),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>w});var a=t(6540),r=t(870),i=t(3104),o=t(6347),s=t(205),l=t(7485),c=t(1682),u=t(9466);function d(e){return a.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,a.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:t}=e;return(0,a.useMemo)((()=>{const e=n??function(e){return d(e).map((e=>{let{props:{value:n,label:t,attributes:a,default:r}}=e;return{value:n,label:t,attributes:a,default:r}}))}(t);return function(e){const n=(0,c.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function p(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function f(e){let{queryString:n=!1,groupId:t}=e;const r=(0,o.W6)(),i=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,l.aZ)(i),(0,a.useCallback)((e=>{if(!i)return;const n=new URLSearchParams(r.location.search);n.set(i,e),r.replace({...r.location,search:n.toString()})}),[i,r])]}function v(e){const{defaultValue:n,queryString:t=!1,groupId:r}=e,i=h(e),[o,l]=(0,a.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const a=t.find((e=>e.default))??t[0];if(!a)throw new Error("Unexpected error: 0 tabValues");return a.value}({defaultValue:n,tabValues:i}))),[c,d]=f({queryString:t,groupId:r}),[v,b]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[r,i]=(0,u.Dv)(t);return[r,(0,a.useCallback)((e=>{t&&i.set(e)}),[t,i])]}({groupId:r}),m=(()=>{const e=c??v;return p({value:e,tabValues:i})?e:null})();(0,s.A)((()=>{m&&l(m)}),[m]);return{selectedValue:o,selectValue:(0,a.useCallback)((e=>{if(!p({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);l(e),d(e),b(e)}),[d,b,i]),tabValues:i}}var b=t(2303);const m={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var g=t(4848);function x(e){let{className:n,block:t,selectedValue:a,selectValue:o,tabValues:s}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,i.a_)(),u=e=>{const n=e.currentTarget,t=l.indexOf(n),r=s[t].value;r!==a&&(c(n),o(r))},d=e=>{let n=null;switch(e.key){case"Enter":u(e);break;case"ArrowRight":{const t=l.indexOf(e.currentTarget)+1;n=l[t]??l[0];break}case"ArrowLeft":{const t=l.indexOf(e.currentTarget)-1;n=l[t]??l[l.length-1];break}}n?.focus()};return(0,g.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":t},n),children:s.map((e=>{let{value:n,label:t,attributes:i}=e;return(0,g.jsx)("li",{role:"tab",tabIndex:a===n?0:-1,"aria-selected":a===n,ref:e=>l.push(e),onKeyDown:d,onClick:u,...i,className:(0,r.A)("tabs__item",m.tabItem,i?.className,{"tabs__item--active":a===n}),children:t??n},n)}))})}function y(e){let{lazy:n,children:t,selectedValue:r}=e;const i=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===r));return e?(0,a.cloneElement)(e,{className:"margin-top--md"}):null}return(0,g.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,a.cloneElement)(e,{key:n,hidden:e.props.value!==r})))})}function j(e){const n=v(e);return(0,g.jsxs)("div",{className:(0,r.A)("tabs-container",m.tabList),children:[(0,g.jsx)(x,{...e,...n}),(0,g.jsx)(y,{...e,...n})]})}function w(e){const n=(0,b.A)();return(0,g.jsx)(j,{...e,children:d(e.children)},String(n))}},8453:(e,n,t)=>{t.d(n,{R:()=>o,x:()=>s});var a=t(6540);const r={},i=a.createContext(r);function o(e){const n=a.useContext(i);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),a.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[371],{9721:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>l,default:()=>p,frontMatter:()=>s,metadata:()=>c,toc:()=>d});var a=t(4848),r=t(8453),i=t(1470),o=t(9365);const s={},l="Special resolution cases",c={id:"advanced/special-resolution-cases",title:"Special resolution cases",description:"Unknown type resolution",source:"@site/docs/advanced/special-resolution-cases.md",sourceDirName:"advanced",slug:"/advanced/special-resolution-cases",permalink:"/stashbox/docs/advanced/special-resolution-cases",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/advanced/special-resolution-cases.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Child containers",permalink:"/stashbox/docs/advanced/child-containers"},next:{title:"Validation",permalink:"/stashbox/docs/diagnostics/validation"}},u={},d=[{value:"Unknown type resolution",id:"unknown-type-resolution",level:2},{value:"Default value injection",id:"default-value-injection",level:2},{value:"Optional value injection",id:"optional-value-injection",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",p:"p",pre:"pre",...(0,r.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.h1,{id:"special-resolution-cases",children:"Special resolution cases"}),"\n",(0,a.jsx)(n.h2,{id:"unknown-type-resolution",children:"Unknown type resolution"}),"\n",(0,a.jsxs)(n.p,{children:["When this ",(0,a.jsx)(n.a,{href:"/docs/configuration/container-configuration#unknown-type-resolution",children:"feature"})," is enabled, the container will try to resolve unregistered types by registering them using a pre-defined configuration delegate."]}),"\n",(0,a.jsxs)(i.A,{children:[(0,a.jsxs)(o.A,{value:"Default",label:"Default",children:[(0,a.jsxs)(n.p,{children:["Without a registration configuration, the container can resolve only non-interface and non-abstract unknown types. In the following example,\nthe container creates an implicit registration for ",(0,a.jsx)(n.code,{children:"Dependency"})," and injects its instance into ",(0,a.jsx)(n.code,{children:"Service"}),"."]}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cs",children:"class Dependency { }\n\nclass Service \n{\n public Service(Dependency dependency)\n { } \n}\n\nvar container = new StashboxContainer(config => config\n .WithUnknownTypeResolution());\n\ncontainer.Register();\n\nvar service = container.Resolve();\n"})})]}),(0,a.jsxs)(o.A,{value:"With registration configuration",label:"With registration configuration",children:[(0,a.jsxs)(n.p,{children:["With a registration configuration, you can control how an unknown type's individual registration should behave. You can also react to a service resolution request. In the following example, we tell the container that if it finds an unregistered ",(0,a.jsx)(n.code,{children:"IDependency"})," for the first time, that should be mapped to ",(0,a.jsx)(n.code,{children:"Dependency"})," and have a singleton lifetime. Next time, when the container comes across this service, it will use the registration created at the first request."]}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cs",children:"interface IDependency { }\n\nclass Dependency : IDependency { }\n\nclass Service \n{\n public Service(IDependency dependency)\n { } \n}\n\nvar container = new StashboxContainer(config => config\n .WithUnknownTypeResolution(options => \n {\n if(options.ServiceType == typeof(IDependency))\n {\n options.SetImplementationType(typeof(Dependency))\n .WithLifetime(Lifetimes.Singleton);\n }\n }));\n\ncontainer.Register();\n\nvar service = container.Resolve();\n"})})]})]}),"\n",(0,a.jsx)(n.h2,{id:"default-value-injection",children:"Default value injection"}),"\n",(0,a.jsxs)(n.p,{children:["When this ",(0,a.jsx)(n.a,{href:"/docs/configuration/container-configuration#default-value-injection",children:"feature"})," is enabled, the container will resolve unknown primitive dependencies with their default value."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cs",children:"class Person \n{\n public Person(string name, int age) { }\n}\n\nvar container = new StashboxContainer(config => config\n .WithDefaultValueInjection());\n// the name parameter will be null and the age will be 0.\nvar person = container.Resolve();\n"})}),"\n",(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsxs)(n.p,{children:["Unknown reference types are resolved to ",(0,a.jsx)(n.code,{children:"null"})," only in properties and fields."]})}),"\n",(0,a.jsx)(n.h2,{id:"optional-value-injection",children:"Optional value injection"}),"\n",(0,a.jsx)(n.p,{children:"Stashbox respects the optional value of each constructor and method argument."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cs",children:"class Person \n{\n public Person(string name = null, int age = 54, IContact contact = null) { }\n}\n\n// the name will be null \n// the age will be 54.\n// the contact will be null.\nvar person = container.Resolve();\n"})})]})}function p(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(h,{...e})}):h(e)}},9365:(e,n,t)=>{t.d(n,{A:()=>o});t(6540);var a=t(870);const r={tabItem:"tabItem_Ymn6"};var i=t(4848);function o(e){let{children:n,hidden:t,className:o}=e;return(0,i.jsx)("div",{role:"tabpanel",className:(0,a.A)(r.tabItem,o),hidden:t,children:n})}},1470:(e,n,t)=>{t.d(n,{A:()=>w});var a=t(6540),r=t(870),i=t(3104),o=t(6347),s=t(205),l=t(7485),c=t(1682),u=t(9466);function d(e){return a.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,a.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:t}=e;return(0,a.useMemo)((()=>{const e=n??function(e){return d(e).map((e=>{let{props:{value:n,label:t,attributes:a,default:r}}=e;return{value:n,label:t,attributes:a,default:r}}))}(t);return function(e){const n=(0,c.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,t])}function p(e){let{value:n,tabValues:t}=e;return t.some((e=>e.value===n))}function f(e){let{queryString:n=!1,groupId:t}=e;const r=(0,o.W6)(),i=function(e){let{queryString:n=!1,groupId:t}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!t)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:n,groupId:t});return[(0,l.aZ)(i),(0,a.useCallback)((e=>{if(!i)return;const n=new URLSearchParams(r.location.search);n.set(i,e),r.replace({...r.location,search:n.toString()})}),[i,r])]}function v(e){const{defaultValue:n,queryString:t=!1,groupId:r}=e,i=h(e),[o,l]=(0,a.useState)((()=>function(e){let{defaultValue:n,tabValues:t}=e;if(0===t.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:t}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const a=t.find((e=>e.default))??t[0];if(!a)throw new Error("Unexpected error: 0 tabValues");return a.value}({defaultValue:n,tabValues:i}))),[c,d]=f({queryString:t,groupId:r}),[v,b]=function(e){let{groupId:n}=e;const t=function(e){return e?`docusaurus.tab.${e}`:null}(n),[r,i]=(0,u.Dv)(t);return[r,(0,a.useCallback)((e=>{t&&i.set(e)}),[t,i])]}({groupId:r}),m=(()=>{const e=c??v;return p({value:e,tabValues:i})?e:null})();(0,s.A)((()=>{m&&l(m)}),[m]);return{selectedValue:o,selectValue:(0,a.useCallback)((e=>{if(!p({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);l(e),d(e),b(e)}),[d,b,i]),tabValues:i}}var b=t(2303);const m={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var g=t(4848);function x(e){let{className:n,block:t,selectedValue:a,selectValue:o,tabValues:s}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,i.a_)(),u=e=>{const n=e.currentTarget,t=l.indexOf(n),r=s[t].value;r!==a&&(c(n),o(r))},d=e=>{let n=null;switch(e.key){case"Enter":u(e);break;case"ArrowRight":{const t=l.indexOf(e.currentTarget)+1;n=l[t]??l[0];break}case"ArrowLeft":{const t=l.indexOf(e.currentTarget)-1;n=l[t]??l[l.length-1];break}}n?.focus()};return(0,g.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":t},n),children:s.map((e=>{let{value:n,label:t,attributes:i}=e;return(0,g.jsx)("li",{role:"tab",tabIndex:a===n?0:-1,"aria-selected":a===n,ref:e=>l.push(e),onKeyDown:d,onClick:u,...i,className:(0,r.A)("tabs__item",m.tabItem,i?.className,{"tabs__item--active":a===n}),children:t??n},n)}))})}function y(e){let{lazy:n,children:t,selectedValue:r}=e;const i=(Array.isArray(t)?t:[t]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===r));return e?(0,a.cloneElement)(e,{className:"margin-top--md"}):null}return(0,g.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,a.cloneElement)(e,{key:n,hidden:e.props.value!==r})))})}function j(e){const n=v(e);return(0,g.jsxs)("div",{className:(0,r.A)("tabs-container",m.tabList),children:[(0,g.jsx)(x,{...e,...n}),(0,g.jsx)(y,{...e,...n})]})}function w(e){const n=(0,b.A)();return(0,g.jsx)(j,{...e,children:d(e.children)},String(n))}},8453:(e,n,t)=>{t.d(n,{R:()=>o,x:()=>s});var a=t(6540);const r={},i=a.createContext(r);function o(e){const n=a.useContext(i);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),a.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/dd45a7f1.115c8e6a.js b/assets/js/dd45a7f1.85193944.js similarity index 98% rename from assets/js/dd45a7f1.115c8e6a.js rename to assets/js/dd45a7f1.85193944.js index 4d01bf4b..064b69be 100644 --- a/assets/js/dd45a7f1.115c8e6a.js +++ b/assets/js/dd45a7f1.85193944.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[109],{1685:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>a,contentTitle:()=>d,default:()=>p,frontMatter:()=>c,metadata:()=>o,toc:()=>l});var s=i(4848),t=i(8453),r=i(7470);const c={},d="Glossary",o={id:"getting-started/glossary",title:"Glossary",description:"The following terms and definitions are used in this documentation.",source:"@site/docs/getting-started/glossary.md",sourceDirName:"getting-started",slug:"/getting-started/glossary",permalink:"/stashbox/docs/getting-started/glossary",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/getting-started/glossary.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Introduction",permalink:"/stashbox/docs/getting-started/introduction"},next:{title:"Basic usage",permalink:"/stashbox/docs/guides/basics"}},a={},l=[{value:"Service type | Implementation type",id:"service-type--implementation-type",level:2},{value:"Service registration | Registered service",id:"service-registration--registered-service",level:2},{value:"Injectable dependency",id:"injectable-dependency",level:2},{value:"Resolution tree",id:"resolution-tree",level:2},{value:"Dependency resolver",id:"dependency-resolver",level:2},{value:"Root scope",id:"root-scope",level:2},{value:"Named resolution",id:"named-resolution",level:2},{value:"Self registration",id:"self-registration",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",p:"p",pre:"pre",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.h1,{id:"glossary",children:"Glossary"}),"\n",(0,s.jsx)(n.p,{children:"The following terms and definitions are used in this documentation."}),"\n",(0,s.jsx)(n.h2,{id:"service-type--implementation-type",children:"Service type | Implementation type"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.em,{children:"Service type"})," is usually an interface or an abstract class type used for service resolution or dependency injection. The ",(0,s.jsx)(n.em,{children:"Implementation type"})," is the actual type registered to the ",(0,s.jsx)(n.em,{children:"Service type"}),". A registration maps the ",(0,s.jsx)(n.em,{children:"Service type"})," to an ",(0,s.jsx)(n.em,{children:"Implementation type"}),". The ",(0,s.jsx)(n.em,{children:"Implementation type"})," must implement or extend the ",(0,s.jsx)(n.em,{children:"Service type"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsxs)(n.p,{children:["Example where a ",(0,s.jsx)(n.em,{children:"Service type"})," is mapped to an ",(0,s.jsx)(n.em,{children:"Implementation type"}),":"]})}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"container.Register();\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.em,{children:"Service type"})," used for requesting a service from the container:"]})}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"container.Resolve(); // returns Implementation\n"})})})]}),"\n",(0,s.jsx)(n.h2,{id:"service-registration--registered-service",children:"Service registration | Registered service"}),"\n",(0,s.jsx)(n.p,{children:"It's an entity created by Stashbox when a service is registered. The service registration stores required information about how to instantiate the service, e.g., reflected type information, name, lifetime, conditions, and more."}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsxs)(n.p,{children:["In this example, we are registering a named service. The container will create a service registration entity to store the type mapping and the name. During resolution, the container will find the registration by checking for the ",(0,s.jsx)(n.em,{children:"Service type"})," and the ",(0,s.jsx)(n.em,{children:"name"}),"."]})}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'// the registration entity will look like: \n// IService => Implementation, name: Example\ncontainer.Register("Example");\nvar service = container.Resolve("Example");\n'})})})]}),"\n",(0,s.jsx)(n.h2,{id:"injectable-dependency",children:"Injectable dependency"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsxs)(n.p,{children:["It's a constructor/method argument or a property/field of a registered ",(0,s.jsx)(n.em,{children:"Implementation type"})," that gets evaluated (",(0,s.jsx)(n.em,{children:"injected"}),") by Stashbox during the service's construction."]}),(0,s.jsxs)(n.p,{children:["In this example, ",(0,s.jsx)(n.code,{children:"Implementation"})," has an ",(0,s.jsx)(n.code,{children:"IDependency"})," ",(0,s.jsx)(n.em,{children:"injectable dependency"})," in its constructor."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"class Implementation : IService\n{\n public Implementation(IDependency dependency) \n { }\n}\n"})})})]}),"\n",(0,s.jsx)(n.h2,{id:"resolution-tree",children:"Resolution tree"}),"\n",(0,s.jsx)(n.p,{children:"It's the structural representation of a service's resolution process. It describes the instantiation order of the dependencies required to resolve the desired type."}),"\n",(0,s.jsx)(n.p,{children:"Let's see through an example:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"class A\n{\n public A(B b, C c) { }\n}\n\nclass B\n{\n public B(C c, D d) { }\n}\n\nclass C { }\nclass D { }\n"})}),"\n",(0,s.jsxs)(n.p,{children:["When we request the service ",(0,s.jsx)(n.code,{children:"A"}),", the container constructs the following resolution tree based on the dependencies and sub-dependencies."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:" A\n / \\\n B C\n / \\\n C D\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The container instantiates those services first that don't have any dependencies. ",(0,s.jsx)(n.code,{children:"C"})," and ",(0,s.jsx)(n.code,{children:"D"})," will be injected into ",(0,s.jsx)(n.code,{children:"B"}),". Then, a new ",(0,s.jsx)(n.code,{children:"C"})," is instantiated (if it's ",(0,s.jsx)(n.a,{href:"/docs/guides/lifetimes#transient-lifetime",children:"transient"}),") and injected into ",(0,s.jsx)(n.code,{children:"A"})," along with the previously created ",(0,s.jsx)(n.code,{children:"B"}),"."]}),"\n",(0,s.jsx)(n.h2,{id:"dependency-resolver",children:"Dependency resolver"}),"\n",(0,s.jsxs)(n.p,{children:["It's the container itself or the ",(0,s.jsx)(n.a,{href:"/docs/guides/scopes",children:"current scope"}),", depending on which was asked to resolve a particular service. They are both implementing Stashbox's ",(0,s.jsx)(n.code,{children:"IDependencyResolver"})," and the .NET framework's ",(0,s.jsx)(n.code,{children:"IServiceProvider"})," interface and can be used for service resolution."]}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["Stashbox implicitly injects the ",(0,s.jsx)(n.a,{href:"/docs/guides/scopes",children:"current scope"})," wherever ",(0,s.jsx)(n.code,{children:"IDependencyResolver"})," or ",(0,s.jsx)(n.code,{children:"IServiceProvider"})," is requested."]})}),"\n",(0,s.jsx)(n.h2,{id:"root-scope",children:"Root scope"}),"\n",(0,s.jsxs)(n.p,{children:["It's the ",(0,s.jsx)(n.a,{href:"/docs/guides/scopes",children:"main scope"})," created inside every container instance. It stores and handles the lifetime of all singletons. It's the base of each subsequent scope created by the container with the ",(0,s.jsx)(n.code,{children:".BeginScope()"})," method."]}),"\n",(0,s.jsx)(n.admonition,{type:"caution",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"/docs/guides/lifetimes#scoped-lifetime",children:"Scoped services"})," requested from the container (and not from a ",(0,s.jsx)(n.a,{href:"/docs/guides/scopes",children:"scope"}),") are managed by the root scope. This can lead to issues because their lifetime will effectively switch to singleton. Always be sure that you don't resolve scoped services directly from the container, only from a ",(0,s.jsx)(n.a,{href:"/docs/guides/scopes",children:"scope"}),". This case is monitored by the ",(0,s.jsx)(n.a,{href:"/docs/diagnostics/validation#lifetime-validation",children:"lifetime"})," validation rule when it's ",(0,s.jsx)(n.a,{href:"/docs/configuration/container-configuration#lifetime-validation",children:"enabled"}),"."]})}),"\n",(0,s.jsx)(n.h2,{id:"named-resolution",children:"Named resolution"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsxs)(n.p,{children:["It's a resolution request for a named service. The same applies, when the container sees a dependency in the resolution tree with a name (set by ",(0,s.jsx)(n.a,{href:"/docs/guides/service-resolution#attributes",children:"attributes"})," or ",(0,s.jsx)(n.a,{href:"/docs/guides/service-resolution#dependency-binding",children:"bindings"}),"); it will search for a matching ",(0,s.jsx)(n.a,{href:"/docs/guides/basics#named-registration",children:"Named registration"})," to inject."]})}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'container.Register("Example");\n// the named request.\nvar service = container.Resolve("Example");\n'})})})]}),"\n",(0,s.jsx)(n.h2,{id:"self-registration",children:"Self registration"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsx)(n.p,{children:"It's a service registration that's mapped to itself. This means its service and implementation type is the same."})}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"// equivalent to container.Register();\ncontainer.Register();\n"})})})]})]})}function p(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}},7470:(e,n,i)=>{i.d(n,{A:()=>c});var s=i(6540);const t={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var r=i(4848);function c(e){let{children:n}=e,i=s.Children.toArray(n).filter((e=>e));return(0,r.jsxs)("div",{className:t.codeDescContainer,children:[(0,r.jsx)("div",{className:t.desc,children:i[0]}),(0,r.jsx)("div",{className:t.example,children:i[1]})]})}},8453:(e,n,i)=>{i.d(n,{R:()=>c,x:()=>d});var s=i(6540);const t={},r=s.createContext(t);function c(e){const n=s.useContext(r);return s.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function d(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:c(e.components),s.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[109],{1685:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>a,contentTitle:()=>d,default:()=>p,frontMatter:()=>c,metadata:()=>o,toc:()=>l});var s=i(4848),t=i(8453),r=i(7470);const c={},d="Glossary",o={id:"getting-started/glossary",title:"Glossary",description:"The following terms and definitions are used in this documentation.",source:"@site/docs/getting-started/glossary.md",sourceDirName:"getting-started",slug:"/getting-started/glossary",permalink:"/stashbox/docs/getting-started/glossary",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/getting-started/glossary.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Introduction",permalink:"/stashbox/docs/getting-started/introduction"},next:{title:"Basic usage",permalink:"/stashbox/docs/guides/basics"}},a={},l=[{value:"Service type | Implementation type",id:"service-type--implementation-type",level:2},{value:"Service registration | Registered service",id:"service-registration--registered-service",level:2},{value:"Injectable dependency",id:"injectable-dependency",level:2},{value:"Resolution tree",id:"resolution-tree",level:2},{value:"Dependency resolver",id:"dependency-resolver",level:2},{value:"Root scope",id:"root-scope",level:2},{value:"Named resolution",id:"named-resolution",level:2},{value:"Self registration",id:"self-registration",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",p:"p",pre:"pre",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.h1,{id:"glossary",children:"Glossary"}),"\n",(0,s.jsx)(n.p,{children:"The following terms and definitions are used in this documentation."}),"\n",(0,s.jsx)(n.h2,{id:"service-type--implementation-type",children:"Service type | Implementation type"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.em,{children:"Service type"})," is usually an interface or an abstract class type used for service resolution or dependency injection. The ",(0,s.jsx)(n.em,{children:"Implementation type"})," is the actual type registered to the ",(0,s.jsx)(n.em,{children:"Service type"}),". A registration maps the ",(0,s.jsx)(n.em,{children:"Service type"})," to an ",(0,s.jsx)(n.em,{children:"Implementation type"}),". The ",(0,s.jsx)(n.em,{children:"Implementation type"})," must implement or extend the ",(0,s.jsx)(n.em,{children:"Service type"}),"."]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsxs)(n.p,{children:["Example where a ",(0,s.jsx)(n.em,{children:"Service type"})," is mapped to an ",(0,s.jsx)(n.em,{children:"Implementation type"}),":"]})}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"container.Register();\n"})})})]}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.em,{children:"Service type"})," used for requesting a service from the container:"]})}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"container.Resolve(); // returns Implementation\n"})})})]}),"\n",(0,s.jsx)(n.h2,{id:"service-registration--registered-service",children:"Service registration | Registered service"}),"\n",(0,s.jsx)(n.p,{children:"It's an entity created by Stashbox when a service is registered. The service registration stores required information about how to instantiate the service, e.g., reflected type information, name, lifetime, conditions, and more."}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsxs)(n.p,{children:["In this example, we are registering a named service. The container will create a service registration entity to store the type mapping and the name. During resolution, the container will find the registration by checking for the ",(0,s.jsx)(n.em,{children:"Service type"})," and the ",(0,s.jsx)(n.em,{children:"name"}),"."]})}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'// the registration entity will look like: \n// IService => Implementation, name: Example\ncontainer.Register("Example");\nvar service = container.Resolve("Example");\n'})})})]}),"\n",(0,s.jsx)(n.h2,{id:"injectable-dependency",children:"Injectable dependency"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsxs)("div",{children:[(0,s.jsxs)(n.p,{children:["It's a constructor/method argument or a property/field of a registered ",(0,s.jsx)(n.em,{children:"Implementation type"})," that gets evaluated (",(0,s.jsx)(n.em,{children:"injected"}),") by Stashbox during the service's construction."]}),(0,s.jsxs)(n.p,{children:["In this example, ",(0,s.jsx)(n.code,{children:"Implementation"})," has an ",(0,s.jsx)(n.code,{children:"IDependency"})," ",(0,s.jsx)(n.em,{children:"injectable dependency"})," in its constructor."]})]}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"class Implementation : IService\n{\n public Implementation(IDependency dependency) \n { }\n}\n"})})})]}),"\n",(0,s.jsx)(n.h2,{id:"resolution-tree",children:"Resolution tree"}),"\n",(0,s.jsx)(n.p,{children:"It's the structural representation of a service's resolution process. It describes the instantiation order of the dependencies required to resolve the desired type."}),"\n",(0,s.jsx)(n.p,{children:"Let's see through an example:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"class A\n{\n public A(B b, C c) { }\n}\n\nclass B\n{\n public B(C c, D d) { }\n}\n\nclass C { }\nclass D { }\n"})}),"\n",(0,s.jsxs)(n.p,{children:["When we request the service ",(0,s.jsx)(n.code,{children:"A"}),", the container constructs the following resolution tree based on the dependencies and sub-dependencies."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:" A\n / \\\n B C\n / \\\n C D\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The container instantiates those services first that don't have any dependencies. ",(0,s.jsx)(n.code,{children:"C"})," and ",(0,s.jsx)(n.code,{children:"D"})," will be injected into ",(0,s.jsx)(n.code,{children:"B"}),". Then, a new ",(0,s.jsx)(n.code,{children:"C"})," is instantiated (if it's ",(0,s.jsx)(n.a,{href:"/docs/guides/lifetimes#transient-lifetime",children:"transient"}),") and injected into ",(0,s.jsx)(n.code,{children:"A"})," along with the previously created ",(0,s.jsx)(n.code,{children:"B"}),"."]}),"\n",(0,s.jsx)(n.h2,{id:"dependency-resolver",children:"Dependency resolver"}),"\n",(0,s.jsxs)(n.p,{children:["It's the container itself or the ",(0,s.jsx)(n.a,{href:"/docs/guides/scopes",children:"current scope"}),", depending on which was asked to resolve a particular service. They are both implementing Stashbox's ",(0,s.jsx)(n.code,{children:"IDependencyResolver"})," and the .NET framework's ",(0,s.jsx)(n.code,{children:"IServiceProvider"})," interface and can be used for service resolution."]}),"\n",(0,s.jsx)(n.admonition,{type:"info",children:(0,s.jsxs)(n.p,{children:["Stashbox implicitly injects the ",(0,s.jsx)(n.a,{href:"/docs/guides/scopes",children:"current scope"})," wherever ",(0,s.jsx)(n.code,{children:"IDependencyResolver"})," or ",(0,s.jsx)(n.code,{children:"IServiceProvider"})," is requested."]})}),"\n",(0,s.jsx)(n.h2,{id:"root-scope",children:"Root scope"}),"\n",(0,s.jsxs)(n.p,{children:["It's the ",(0,s.jsx)(n.a,{href:"/docs/guides/scopes",children:"main scope"})," created inside every container instance. It stores and handles the lifetime of all singletons. It's the base of each subsequent scope created by the container with the ",(0,s.jsx)(n.code,{children:".BeginScope()"})," method."]}),"\n",(0,s.jsx)(n.admonition,{type:"caution",children:(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"/docs/guides/lifetimes#scoped-lifetime",children:"Scoped services"})," requested from the container (and not from a ",(0,s.jsx)(n.a,{href:"/docs/guides/scopes",children:"scope"}),") are managed by the root scope. This can lead to issues because their lifetime will effectively switch to singleton. Always be sure that you don't resolve scoped services directly from the container, only from a ",(0,s.jsx)(n.a,{href:"/docs/guides/scopes",children:"scope"}),". This case is monitored by the ",(0,s.jsx)(n.a,{href:"/docs/diagnostics/validation#lifetime-validation",children:"lifetime"})," validation rule when it's ",(0,s.jsx)(n.a,{href:"/docs/configuration/container-configuration#lifetime-validation",children:"enabled"}),"."]})}),"\n",(0,s.jsx)(n.h2,{id:"named-resolution",children:"Named resolution"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsxs)(n.p,{children:["It's a resolution request for a named service. The same applies, when the container sees a dependency in the resolution tree with a name (set by ",(0,s.jsx)(n.a,{href:"/docs/guides/service-resolution#attributes",children:"attributes"})," or ",(0,s.jsx)(n.a,{href:"/docs/guides/service-resolution#dependency-binding",children:"bindings"}),"); it will search for a matching ",(0,s.jsx)(n.a,{href:"/docs/guides/basics#named-registration",children:"Named registration"})," to inject."]})}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:'container.Register("Example");\n// the named request.\nvar service = container.Resolve("Example");\n'})})})]}),"\n",(0,s.jsx)(n.h2,{id:"self-registration",children:"Self registration"}),"\n",(0,s.jsxs)(r.A,{children:[(0,s.jsx)("div",{children:(0,s.jsx)(n.p,{children:"It's a service registration that's mapped to itself. This means its service and implementation type is the same."})}),(0,s.jsx)("div",{children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-cs",children:"// equivalent to container.Register();\ncontainer.Register();\n"})})})]})]})}function p(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}},7470:(e,n,i)=>{i.d(n,{A:()=>c});var s=i(6540);const t={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var r=i(4848);function c(e){let{children:n}=e,i=s.Children.toArray(n).filter((e=>e));return(0,r.jsxs)("div",{className:t.codeDescContainer,children:[(0,r.jsx)("div",{className:t.desc,children:i[0]}),(0,r.jsx)("div",{className:t.example,children:i[1]})]})}},8453:(e,n,i)=>{i.d(n,{R:()=>c,x:()=>d});var s=i(6540);const t={},r=s.createContext(t);function c(e){const n=s.useContext(r);return s.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function d(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:c(e.components),s.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/fd379919.463a8430.js b/assets/js/fd379919.6f340728.js similarity index 99% rename from assets/js/fd379919.463a8430.js rename to assets/js/fd379919.6f340728.js index 2e2323ea..fd2134f5 100644 --- a/assets/js/fd379919.463a8430.js +++ b/assets/js/fd379919.6f340728.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[495],{1267:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>u,contentTitle:()=>l,default:()=>b,frontMatter:()=>c,metadata:()=>d,toc:()=>h});var t=s(4848),i=s(8453),r=s(7470),a=s(1470),o=s(9365);const c={},l="Basic usage",d={id:"guides/basics",title:"Basic usage",description:"This section is about the basics of Stashbox's API. It will give you a good starting point for more advanced topics described in the following sections.",source:"@site/docs/guides/basics.md",sourceDirName:"guides",slug:"/guides/basics",permalink:"/stashbox/docs/guides/basics",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/guides/basics.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Glossary",permalink:"/stashbox/docs/getting-started/glossary"},next:{title:"Advanced registration",permalink:"/stashbox/docs/guides/advanced-registration"}},u={},h=[{value:"Default registration",id:"default-registration",level:2},{value:"Named registration",id:"named-registration",level:2},{value:"Instance registration",id:"instance-registration",level:2},{value:"Re-mapping",id:"re-mapping",level:2},{value:"Wiring up",id:"wiring-up",level:2},{value:"Lifetime shortcuts",id:"lifetime-shortcuts",level:2}];function p(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",p:"p",pre:"pre",...(0,i.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{id:"basic-usage",children:"Basic usage"}),"\n",(0,t.jsx)(n.p,{children:"This section is about the basics of Stashbox's API. It will give you a good starting point for more advanced topics described in the following sections.\nStashbox provides several methods that enable registering services, and we'll go through the most common scenarios with code examples."}),"\n",(0,t.jsx)(n.h2,{id:"default-registration",children:"Default registration"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["Stashbox allows registration operations via the ",(0,t.jsx)(n.code,{children:"Register()"})," methods."]}),(0,t.jsxs)(n.p,{children:["During registration, the container checks whether the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," is assignable from the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," and if not, the container throws an ",(0,t.jsx)(n.a,{href:"/docs/diagnostics/validation#registration-validation",children:"exception"}),"."]}),(0,t.jsxs)(n.p,{children:["Also, when the implementation is not resolvable, the container throws the same ",(0,t.jsx)(n.a,{href:"/docs/diagnostics/validation#registration-validation",children:"exception"}),"."]}),(0,t.jsxs)(n.p,{children:["The example registers ",(0,t.jsx)(n.code,{children:"DbBackup"})," to be returned when ",(0,t.jsx)(n.code,{children:"IJob"})," is requested."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(o.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\nIJob job = container.Resolve();\n// throws an exception because ConsoleLogger doesn't implement IJob.\ncontainer.Register();\n// throws an exception because IJob is not a valid implementation.\ncontainer.Register();\n"})})}),(0,t.jsx)(o.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(typeof(IJob), typeof(DbBackup));\nobject job = container.Resolve(typeof(IJob));\n// throws an exception because ConsoleLogger doesn't implement IJob.\ncontainer.Register(typeof(IJob), typeof(ConsoleLogger));\n// throws an exception because IJob is not a valid implementation.\ncontainer.Register(typeof(IJob), typeof(IJob));\n"})})})]})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["You can register a service to itself without specifying a ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),", only the implementation (",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#self-registration",children:"self registration"}),")."]}),(0,t.jsxs)(n.p,{children:["In this case, the given implementation is considered the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," and must be used to request the service (",(0,t.jsx)(n.code,{children:"DbBackup"})," in the example)."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(o.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\nDbBackup backup = container.Resolve();\n"})})}),(0,t.jsx)(o.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(typeof(DbBackup));\nobject backup = container.Resolve(typeof(DbBackup));\n"})})})]})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"The container's API is fluent, which means you can chain the calls on its methods after each other."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"var job = container.Register()\n .Register()\n .Resolve();\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"named-registration",children:"Named registration"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["The example shows how you can bind more implementations to a ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," using names for identification."]}),(0,t.jsx)(n.p,{children:"The same name must be used to resolve the named service."}),(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["The name is an ",(0,t.jsx)(n.code,{children:"object"})," type."]})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(o.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.Register("DbBackup");\ncontainer.Register("StorageCleanup");\nIJob cleanup = container.Resolve("StorageCleanup");\n'})})}),(0,t.jsx)(o.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.Register(typeof(IJob), typeof(DbBackup), "DbBackup");\ncontainer.Register(typeof(IJob), typeof(StorageCleanup), "StorageCleanup");\nobject cleanup = container.Resolve(typeof(IJob), "StorageCleanup");\n'})})})]})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["You can also get each service that share the same name by requesting an ",(0,t.jsx)(n.code,{children:"IEnumerable<>"})," or using the ",(0,t.jsx)(n.code,{children:"ResolveAll()"})," method with the ",(0,t.jsx)(n.code,{children:"name"})," parameter."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.Register("StorageJobs");\ncontainer.Register("StorageJobs");\ncontainer.Register();\n// jobs will be [DbBackup, StorageCleanup].\nIEnumerable jobs = container.Resolve>("StorageJobs");\n'})})})]}),"\n",(0,t.jsx)(n.h2,{id:"instance-registration",children:"Instance registration"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["With instance registration, you can provide an already created external instance to use when the given ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," is requested."]}),(0,t.jsxs)(n.p,{children:["Stashbox automatically handles the ",(0,t.jsx)(n.a,{href:"/docs/guides/scopes#disposal",children:"disposal"})," of the registered instances, but you can turn this feature off with the ",(0,t.jsx)(n.code,{children:"withoutDisposalTracking"})," parameter."]}),(0,t.jsxs)(n.p,{children:["When an ",(0,t.jsx)(n.code,{children:"IJob"})," is requested, the container will always return the external instance."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(o.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"var job = new DbBackup();\ncontainer.RegisterInstance(job);\n\n// resolvedJob and job are the same.\nIJob resolvedJob = container.Resolve();\n"})})}),(0,t.jsx)(o.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"var job = new DbBackup();\ncontainer.RegisterInstance(job, typeof(IJob));\n\n// resolvedJob and job are the same.\nobject resolvedJob = container.Resolve(typeof(IJob));\n"})})}),(0,t.jsx)(o.A,{value:"Named",label:"Named",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'var job = new DbBackup();\ncontainer.RegisterInstance(job, "DbBackup");\n\n// resolvedJob and job are the same.\nIJob resolvedJob = container.Resolve("DbBackup");\n'})})}),(0,t.jsx)(o.A,{value:"No dispose",label:"No dispose",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"var job = new DbBackup();\ncontainer.RegisterInstance(job, withoutDisposalTracking: true);\n\n// resolvedJob and job are the same.\nIJob resolvedJob = container.Resolve();\n"})})})]})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"The instance registration API allows the batched registration of different instances."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterInstances(new DbBackup(), new StorageCleanup());\nIEnumerable jobs = container.ResolveAll();\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"re-mapping",children:"Re-mapping"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["With re-map, you can bind new implementations to a ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," and delete old registrations in one action."]}),(0,t.jsx)(n.admonition,{type:"caution",children:(0,t.jsxs)(n.p,{children:["When there are multiple registrations mapped to a ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),", ",(0,t.jsx)(n.code,{children:".ReMap()"})," will replace all of them with the given ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"}),". If you want to replace only one specific service, use the ",(0,t.jsx)(n.code,{children:".ReplaceExisting()"})," ",(0,t.jsx)(n.a,{href:"/docs/configuration/registration-configuration#replace",children:"configuration option"}),"."]})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(o.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\ncontainer.ReMap();\n// jobs contain all two jobs\nIEnumerable jobs = container.ResolveAll();\n\ncontainer.ReMap();\n// jobs contains only the SlackMessageSender\njobs = container.ResolveAll();\n"})})}),(0,t.jsx)(o.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(typeof(IJob), typeof(DbBackup));\ncontainer.Register(typeof(IJob), typeof(StorageCleanup));\n// jobs contain all two jobs\nIEnumerable jobs = container.ResolveAll(typeof(IJob));\n\ncontainer.ReMap(typeof(IJob), typeof(SlackMessageSender));\n// jobs contains only the SlackMessageSender\njobs = container.ResolveAll(typeof(IJob));\n"})})})]})})]}),"\n",(0,t.jsx)(n.h2,{id:"wiring-up",children:"Wiring up"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["Wiring up is similar to ",(0,t.jsx)(n.a,{href:"#instance-registration",children:"Instance registration"})," except that the container will perform property/field injection (if configured so and applicable) on the registered instance during resolution."]})}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(o.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.WireUp(new DbBackup());\nIJob job = container.Resolve();\n"})})}),(0,t.jsx)(o.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.WireUp(new DbBackup(), typeof(IJob));\nobject job = container.Resolve(typeof(IJob));\n"})})})]})})]}),"\n",(0,t.jsx)(n.h2,{id:"lifetime-shortcuts",children:"Lifetime shortcuts"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"A service's lifetime indicates how long its instance will live and which re-using policy should be applied when it gets injected."}),(0,t.jsxs)(n.p,{children:["This example shows how you can use the registration API's shortcuts for lifetimes. These are just sugars, and there are more ways explained in the ",(0,t.jsx)(n.a,{href:"/docs/guides/lifetimes",children:"lifetimes"})," section."]}),(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.code,{children:"DefaultLifetime"})," is ",(0,t.jsx)(n.a,{href:"/docs/guides/lifetimes#default-lifetime",children:"configurable"}),"."]})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsxs)(o.A,{value:"Default",label:"Default",children:[(0,t.jsxs)(n.p,{children:["When no lifetime is specified, the service will use the container's ",(0,t.jsx)(n.code,{children:"DefaultLifetime"}),", which is ",(0,t.jsx)(n.code,{children:"Transient"})," by default."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\nIJob job = container.Resolve();\n"})})]}),(0,t.jsxs)(o.A,{value:"Singleton",label:"Singleton",children:[(0,t.jsxs)(n.p,{children:["A service with ",(0,t.jsx)(n.code,{children:"Singleton"})," lifetime will be instantiated once and reused during the container's lifetime."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterSingleton();\nIJob job = container.Resolve();\n"})})]}),(0,t.jsxs)(o.A,{value:"Scoped",label:"Scoped",children:[(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.code,{children:"Scoped"})," lifetime behaves like a ",(0,t.jsx)(n.code,{children:"Singleton"})," within a ",(0,t.jsx)(n.a,{href:"/docs/guides/scopes",children:"scope"}),".\nA scoped service is instantiated once and reused during the scope's whole lifetime."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterScoped();\nIJob job = container.Resolve();\n"})})]})]})})]})]})}function b(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(p,{...e})}):p(e)}},9365:(e,n,s)=>{s.d(n,{A:()=>a});s(6540);var t=s(870);const i={tabItem:"tabItem_Ymn6"};var r=s(4848);function a(e){let{children:n,hidden:s,className:a}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,t.A)(i.tabItem,a),hidden:s,children:n})}},1470:(e,n,s)=>{s.d(n,{A:()=>y});var t=s(6540),i=s(870),r=s(3104),a=s(6347),o=s(205),c=s(7485),l=s(1682),d=s(9466);function u(e){return t.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,t.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:s}=e;return(0,t.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:s,attributes:t,default:i}}=e;return{value:n,label:s,attributes:t,default:i}}))}(s);return function(e){const n=(0,l.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,s])}function p(e){let{value:n,tabValues:s}=e;return s.some((e=>e.value===n))}function b(e){let{queryString:n=!1,groupId:s}=e;const i=(0,a.W6)(),r=function(e){let{queryString:n=!1,groupId:s}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!s)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return s??null}({queryString:n,groupId:s});return[(0,c.aZ)(r),(0,t.useCallback)((e=>{if(!r)return;const n=new URLSearchParams(i.location.search);n.set(r,e),i.replace({...i.location,search:n.toString()})}),[r,i])]}function g(e){const{defaultValue:n,queryString:s=!1,groupId:i}=e,r=h(e),[a,c]=(0,t.useState)((()=>function(e){let{defaultValue:n,tabValues:s}=e;if(0===s.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:s}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${s.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const t=s.find((e=>e.default))??s[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:r}))),[l,u]=b({queryString:s,groupId:i}),[g,m]=function(e){let{groupId:n}=e;const s=function(e){return e?`docusaurus.tab.${e}`:null}(n),[i,r]=(0,d.Dv)(s);return[i,(0,t.useCallback)((e=>{s&&r.set(e)}),[s,r])]}({groupId:i}),j=(()=>{const e=l??g;return p({value:e,tabValues:r})?e:null})();(0,o.A)((()=>{j&&c(j)}),[j]);return{selectedValue:a,selectValue:(0,t.useCallback)((e=>{if(!p({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);c(e),u(e),m(e)}),[u,m,r]),tabValues:r}}var m=s(2303);const j={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var x=s(4848);function v(e){let{className:n,block:s,selectedValue:t,selectValue:a,tabValues:o}=e;const c=[],{blockElementScrollPositionUntilNextRender:l}=(0,r.a_)(),d=e=>{const n=e.currentTarget,s=c.indexOf(n),i=o[s].value;i!==t&&(l(n),a(i))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const s=c.indexOf(e.currentTarget)+1;n=c[s]??c[0];break}case"ArrowLeft":{const s=c.indexOf(e.currentTarget)-1;n=c[s]??c[c.length-1];break}}n?.focus()};return(0,x.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,i.A)("tabs",{"tabs--block":s},n),children:o.map((e=>{let{value:n,label:s,attributes:r}=e;return(0,x.jsx)("li",{role:"tab",tabIndex:t===n?0:-1,"aria-selected":t===n,ref:e=>c.push(e),onKeyDown:u,onClick:d,...r,className:(0,i.A)("tabs__item",j.tabItem,r?.className,{"tabs__item--active":t===n}),children:s??n},n)}))})}function f(e){let{lazy:n,children:s,selectedValue:i}=e;const r=(Array.isArray(s)?s:[s]).filter(Boolean);if(n){const e=r.find((e=>e.props.value===i));return e?(0,t.cloneElement)(e,{className:"margin-top--md"}):null}return(0,x.jsx)("div",{className:"margin-top--md",children:r.map(((e,n)=>(0,t.cloneElement)(e,{key:n,hidden:e.props.value!==i})))})}function I(e){const n=g(e);return(0,x.jsxs)("div",{className:(0,i.A)("tabs-container",j.tabList),children:[(0,x.jsx)(v,{...e,...n}),(0,x.jsx)(f,{...e,...n})]})}function y(e){const n=(0,m.A)();return(0,x.jsx)(I,{...e,children:u(e.children)},String(n))}},7470:(e,n,s)=>{s.d(n,{A:()=>a});var t=s(6540);const i={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var r=s(4848);function a(e){let{children:n}=e,s=t.Children.toArray(n).filter((e=>e));return(0,r.jsxs)("div",{className:i.codeDescContainer,children:[(0,r.jsx)("div",{className:i.desc,children:s[0]}),(0,r.jsx)("div",{className:i.example,children:s[1]})]})}},8453:(e,n,s)=>{s.d(n,{R:()=>a,x:()=>o});var t=s(6540);const i={},r=t.createContext(i);function a(e){const n=t.useContext(r);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),t.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[495],{1267:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>u,contentTitle:()=>l,default:()=>b,frontMatter:()=>c,metadata:()=>d,toc:()=>h});var t=s(4848),i=s(8453),r=s(7470),a=s(1470),o=s(9365);const c={},l="Basic usage",d={id:"guides/basics",title:"Basic usage",description:"This section is about the basics of Stashbox's API. It will give you a good starting point for more advanced topics described in the following sections.",source:"@site/docs/guides/basics.md",sourceDirName:"guides",slug:"/guides/basics",permalink:"/stashbox/docs/guides/basics",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/guides/basics.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Glossary",permalink:"/stashbox/docs/getting-started/glossary"},next:{title:"Advanced registration",permalink:"/stashbox/docs/guides/advanced-registration"}},u={},h=[{value:"Default registration",id:"default-registration",level:2},{value:"Named registration",id:"named-registration",level:2},{value:"Instance registration",id:"instance-registration",level:2},{value:"Re-mapping",id:"re-mapping",level:2},{value:"Wiring up",id:"wiring-up",level:2},{value:"Lifetime shortcuts",id:"lifetime-shortcuts",level:2}];function p(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",p:"p",pre:"pre",...(0,i.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h1,{id:"basic-usage",children:"Basic usage"}),"\n",(0,t.jsx)(n.p,{children:"This section is about the basics of Stashbox's API. It will give you a good starting point for more advanced topics described in the following sections.\nStashbox provides several methods that enable registering services, and we'll go through the most common scenarios with code examples."}),"\n",(0,t.jsx)(n.h2,{id:"default-registration",children:"Default registration"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["Stashbox allows registration operations via the ",(0,t.jsx)(n.code,{children:"Register()"})," methods."]}),(0,t.jsxs)(n.p,{children:["During registration, the container checks whether the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," is assignable from the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"})," and if not, the container throws an ",(0,t.jsx)(n.a,{href:"/docs/diagnostics/validation#registration-validation",children:"exception"}),"."]}),(0,t.jsxs)(n.p,{children:["Also, when the implementation is not resolvable, the container throws the same ",(0,t.jsx)(n.a,{href:"/docs/diagnostics/validation#registration-validation",children:"exception"}),"."]}),(0,t.jsxs)(n.p,{children:["The example registers ",(0,t.jsx)(n.code,{children:"DbBackup"})," to be returned when ",(0,t.jsx)(n.code,{children:"IJob"})," is requested."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(o.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\nIJob job = container.Resolve();\n// throws an exception because ConsoleLogger doesn't implement IJob.\ncontainer.Register();\n// throws an exception because IJob is not a valid implementation.\ncontainer.Register();\n"})})}),(0,t.jsx)(o.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(typeof(IJob), typeof(DbBackup));\nobject job = container.Resolve(typeof(IJob));\n// throws an exception because ConsoleLogger doesn't implement IJob.\ncontainer.Register(typeof(IJob), typeof(ConsoleLogger));\n// throws an exception because IJob is not a valid implementation.\ncontainer.Register(typeof(IJob), typeof(IJob));\n"})})})]})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["You can register a service to itself without specifying a ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),", only the implementation (",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#self-registration",children:"self registration"}),")."]}),(0,t.jsxs)(n.p,{children:["In this case, the given implementation is considered the ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," and must be used to request the service (",(0,t.jsx)(n.code,{children:"DbBackup"})," in the example)."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(o.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\nDbBackup backup = container.Resolve();\n"})})}),(0,t.jsx)(o.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(typeof(DbBackup));\nobject backup = container.Resolve(typeof(DbBackup));\n"})})})]})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"The container's API is fluent, which means you can chain the calls on its methods after each other."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"var job = container.Register()\n .Register()\n .Resolve();\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"named-registration",children:"Named registration"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["The example shows how you can bind more implementations to a ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," using names for identification."]}),(0,t.jsx)(n.p,{children:"The same name must be used to resolve the named service."}),(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["The name is an ",(0,t.jsx)(n.code,{children:"object"})," type."]})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(o.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.Register("DbBackup");\ncontainer.Register("StorageCleanup");\nIJob cleanup = container.Resolve("StorageCleanup");\n'})})}),(0,t.jsx)(o.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.Register(typeof(IJob), typeof(DbBackup), "DbBackup");\ncontainer.Register(typeof(IJob), typeof(StorageCleanup), "StorageCleanup");\nobject cleanup = container.Resolve(typeof(IJob), "StorageCleanup");\n'})})})]})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["You can also get each service that share the same name by requesting an ",(0,t.jsx)(n.code,{children:"IEnumerable<>"})," or using the ",(0,t.jsx)(n.code,{children:"ResolveAll()"})," method with the ",(0,t.jsx)(n.code,{children:"name"})," parameter."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'container.Register("StorageJobs");\ncontainer.Register("StorageJobs");\ncontainer.Register();\n// jobs will be [DbBackup, StorageCleanup].\nIEnumerable jobs = container.Resolve>("StorageJobs");\n'})})})]}),"\n",(0,t.jsx)(n.h2,{id:"instance-registration",children:"Instance registration"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["With instance registration, you can provide an already created external instance to use when the given ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," is requested."]}),(0,t.jsxs)(n.p,{children:["Stashbox automatically handles the ",(0,t.jsx)(n.a,{href:"/docs/guides/scopes#disposal",children:"disposal"})," of the registered instances, but you can turn this feature off with the ",(0,t.jsx)(n.code,{children:"withoutDisposalTracking"})," parameter."]}),(0,t.jsxs)(n.p,{children:["When an ",(0,t.jsx)(n.code,{children:"IJob"})," is requested, the container will always return the external instance."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(o.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"var job = new DbBackup();\ncontainer.RegisterInstance(job);\n\n// resolvedJob and job are the same.\nIJob resolvedJob = container.Resolve();\n"})})}),(0,t.jsx)(o.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"var job = new DbBackup();\ncontainer.RegisterInstance(job, typeof(IJob));\n\n// resolvedJob and job are the same.\nobject resolvedJob = container.Resolve(typeof(IJob));\n"})})}),(0,t.jsx)(o.A,{value:"Named",label:"Named",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:'var job = new DbBackup();\ncontainer.RegisterInstance(job, "DbBackup");\n\n// resolvedJob and job are the same.\nIJob resolvedJob = container.Resolve("DbBackup");\n'})})}),(0,t.jsx)(o.A,{value:"No dispose",label:"No dispose",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"var job = new DbBackup();\ncontainer.RegisterInstance(job, withoutDisposalTracking: true);\n\n// resolvedJob and job are the same.\nIJob resolvedJob = container.Resolve();\n"})})})]})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(n.p,{children:"The instance registration API allows the batched registration of different instances."})}),(0,t.jsx)("div",{children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterInstances(new DbBackup(), new StorageCleanup());\nIEnumerable jobs = container.ResolveAll();\n"})})})]}),"\n",(0,t.jsx)(n.h2,{id:"re-mapping",children:"Re-mapping"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(n.p,{children:["With re-map, you can bind new implementations to a ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"})," and delete old registrations in one action."]}),(0,t.jsx)(n.admonition,{type:"caution",children:(0,t.jsxs)(n.p,{children:["When there are multiple registrations mapped to a ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"service type"}),", ",(0,t.jsx)(n.code,{children:".ReMap()"})," will replace all of them with the given ",(0,t.jsx)(n.a,{href:"/docs/getting-started/glossary#service-type--implementation-type",children:"implementation type"}),". If you want to replace only one specific service, use the ",(0,t.jsx)(n.code,{children:".ReplaceExisting()"})," ",(0,t.jsx)(n.a,{href:"/docs/configuration/registration-configuration#replace",children:"configuration option"}),"."]})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(o.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\ncontainer.ReMap();\n// jobs contain all two jobs\nIEnumerable jobs = container.ResolveAll();\n\ncontainer.ReMap();\n// jobs contains only the SlackMessageSender\njobs = container.ResolveAll();\n"})})}),(0,t.jsx)(o.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register(typeof(IJob), typeof(DbBackup));\ncontainer.Register(typeof(IJob), typeof(StorageCleanup));\n// jobs contain all two jobs\nIEnumerable jobs = container.ResolveAll(typeof(IJob));\n\ncontainer.ReMap(typeof(IJob), typeof(SlackMessageSender));\n// jobs contains only the SlackMessageSender\njobs = container.ResolveAll(typeof(IJob));\n"})})})]})})]}),"\n",(0,t.jsx)(n.h2,{id:"wiring-up",children:"Wiring up"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(n.p,{children:["Wiring up is similar to ",(0,t.jsx)(n.a,{href:"#instance-registration",children:"Instance registration"})," except that the container will perform property/field injection (if configured so and applicable) on the registered instance during resolution."]})}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsx)(o.A,{value:"Generic API",label:"Generic API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.WireUp(new DbBackup());\nIJob job = container.Resolve();\n"})})}),(0,t.jsx)(o.A,{value:"Runtime type API",label:"Runtime type API",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.WireUp(new DbBackup(), typeof(IJob));\nobject job = container.Resolve(typeof(IJob));\n"})})})]})})]}),"\n",(0,t.jsx)(n.h2,{id:"lifetime-shortcuts",children:"Lifetime shortcuts"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"A service's lifetime indicates how long its instance will live and which re-using policy should be applied when it gets injected."}),(0,t.jsxs)(n.p,{children:["This example shows how you can use the registration API's shortcuts for lifetimes. These are just sugars, and there are more ways explained in the ",(0,t.jsx)(n.a,{href:"/docs/guides/lifetimes",children:"lifetimes"})," section."]}),(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.code,{children:"DefaultLifetime"})," is ",(0,t.jsx)(n.a,{href:"/docs/guides/lifetimes#default-lifetime",children:"configurable"}),"."]})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(a.A,{groupId:"generic-runtime-apis",children:[(0,t.jsxs)(o.A,{value:"Default",label:"Default",children:[(0,t.jsxs)(n.p,{children:["When no lifetime is specified, the service will use the container's ",(0,t.jsx)(n.code,{children:"DefaultLifetime"}),", which is ",(0,t.jsx)(n.code,{children:"Transient"})," by default."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.Register();\nIJob job = container.Resolve();\n"})})]}),(0,t.jsxs)(o.A,{value:"Singleton",label:"Singleton",children:[(0,t.jsxs)(n.p,{children:["A service with ",(0,t.jsx)(n.code,{children:"Singleton"})," lifetime will be instantiated once and reused during the container's lifetime."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterSingleton();\nIJob job = container.Resolve();\n"})})]}),(0,t.jsxs)(o.A,{value:"Scoped",label:"Scoped",children:[(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.code,{children:"Scoped"})," lifetime behaves like a ",(0,t.jsx)(n.code,{children:"Singleton"})," within a ",(0,t.jsx)(n.a,{href:"/docs/guides/scopes",children:"scope"}),".\nA scoped service is instantiated once and reused during the scope's whole lifetime."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-cs",children:"container.RegisterScoped();\nIJob job = container.Resolve();\n"})})]})]})})]})]})}function b(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(p,{...e})}):p(e)}},9365:(e,n,s)=>{s.d(n,{A:()=>a});s(6540);var t=s(870);const i={tabItem:"tabItem_Ymn6"};var r=s(4848);function a(e){let{children:n,hidden:s,className:a}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,t.A)(i.tabItem,a),hidden:s,children:n})}},1470:(e,n,s)=>{s.d(n,{A:()=>y});var t=s(6540),i=s(870),r=s(3104),a=s(6347),o=s(205),c=s(7485),l=s(1682),d=s(9466);function u(e){return t.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,t.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:s}=e;return(0,t.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:s,attributes:t,default:i}}=e;return{value:n,label:s,attributes:t,default:i}}))}(s);return function(e){const n=(0,l.X)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,s])}function p(e){let{value:n,tabValues:s}=e;return s.some((e=>e.value===n))}function b(e){let{queryString:n=!1,groupId:s}=e;const i=(0,a.W6)(),r=function(e){let{queryString:n=!1,groupId:s}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!s)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return s??null}({queryString:n,groupId:s});return[(0,c.aZ)(r),(0,t.useCallback)((e=>{if(!r)return;const n=new URLSearchParams(i.location.search);n.set(r,e),i.replace({...i.location,search:n.toString()})}),[r,i])]}function g(e){const{defaultValue:n,queryString:s=!1,groupId:i}=e,r=h(e),[a,c]=(0,t.useState)((()=>function(e){let{defaultValue:n,tabValues:s}=e;if(0===s.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!p({value:n,tabValues:s}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${s.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const t=s.find((e=>e.default))??s[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:r}))),[l,u]=b({queryString:s,groupId:i}),[g,m]=function(e){let{groupId:n}=e;const s=function(e){return e?`docusaurus.tab.${e}`:null}(n),[i,r]=(0,d.Dv)(s);return[i,(0,t.useCallback)((e=>{s&&r.set(e)}),[s,r])]}({groupId:i}),j=(()=>{const e=l??g;return p({value:e,tabValues:r})?e:null})();(0,o.A)((()=>{j&&c(j)}),[j]);return{selectedValue:a,selectValue:(0,t.useCallback)((e=>{if(!p({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);c(e),u(e),m(e)}),[u,m,r]),tabValues:r}}var m=s(2303);const j={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var x=s(4848);function v(e){let{className:n,block:s,selectedValue:t,selectValue:a,tabValues:o}=e;const c=[],{blockElementScrollPositionUntilNextRender:l}=(0,r.a_)(),d=e=>{const n=e.currentTarget,s=c.indexOf(n),i=o[s].value;i!==t&&(l(n),a(i))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const s=c.indexOf(e.currentTarget)+1;n=c[s]??c[0];break}case"ArrowLeft":{const s=c.indexOf(e.currentTarget)-1;n=c[s]??c[c.length-1];break}}n?.focus()};return(0,x.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,i.A)("tabs",{"tabs--block":s},n),children:o.map((e=>{let{value:n,label:s,attributes:r}=e;return(0,x.jsx)("li",{role:"tab",tabIndex:t===n?0:-1,"aria-selected":t===n,ref:e=>c.push(e),onKeyDown:u,onClick:d,...r,className:(0,i.A)("tabs__item",j.tabItem,r?.className,{"tabs__item--active":t===n}),children:s??n},n)}))})}function f(e){let{lazy:n,children:s,selectedValue:i}=e;const r=(Array.isArray(s)?s:[s]).filter(Boolean);if(n){const e=r.find((e=>e.props.value===i));return e?(0,t.cloneElement)(e,{className:"margin-top--md"}):null}return(0,x.jsx)("div",{className:"margin-top--md",children:r.map(((e,n)=>(0,t.cloneElement)(e,{key:n,hidden:e.props.value!==i})))})}function I(e){const n=g(e);return(0,x.jsxs)("div",{className:(0,i.A)("tabs-container",j.tabList),children:[(0,x.jsx)(v,{...e,...n}),(0,x.jsx)(f,{...e,...n})]})}function y(e){const n=(0,m.A)();return(0,x.jsx)(I,{...e,children:u(e.children)},String(n))}},7470:(e,n,s)=>{s.d(n,{A:()=>a});var t=s(6540);const i={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var r=s(4848);function a(e){let{children:n}=e,s=t.Children.toArray(n).filter((e=>e));return(0,r.jsxs)("div",{className:i.codeDescContainer,children:[(0,r.jsx)("div",{className:i.desc,children:s[0]}),(0,r.jsx)("div",{className:i.example,children:s[1]})]})}},8453:(e,n,s)=>{s.d(n,{R:()=>a,x:()=>o});var t=s(6540);const i={},r=t.createContext(i);function a(e){const n=t.useContext(r);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),t.createElement(r.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/ff10094c.2763c19f.js b/assets/js/ff10094c.ccd23fc1.js similarity index 99% rename from assets/js/ff10094c.2763c19f.js rename to assets/js/ff10094c.ccd23fc1.js index 190fa592..c1259b95 100644 --- a/assets/js/ff10094c.2763c19f.js +++ b/assets/js/ff10094c.ccd23fc1.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[338],{8594:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>u,contentTitle:()=>c,default:()=>f,frontMatter:()=>l,metadata:()=>d,toc:()=>h});var t=n(4848),s=n(8453),r=n(7470),o=n(1470),a=n(9365);const l={},c="Lifetimes",d={id:"guides/lifetimes",title:"Lifetimes",description:"Lifetime management controls how long a service's instances will live (from instantiation to disposal) and how they will be reused between resolution requests.",source:"@site/docs/guides/lifetimes.md",sourceDirName:"guides",slug:"/guides/lifetimes",permalink:"/stashbox/docs/guides/lifetimes",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/guides/lifetimes.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1732321589,formattedLastUpdatedAt:"Nov 23, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Service resolution",permalink:"/stashbox/docs/guides/service-resolution"},next:{title:"Scopes",permalink:"/stashbox/docs/guides/scopes"}},u={},h=[{value:"Default lifetime",id:"default-lifetime",level:2},{value:"Transient lifetime",id:"transient-lifetime",level:2},{value:"Singleton lifetime",id:"singleton-lifetime",level:2},{value:"Scoped lifetime",id:"scoped-lifetime",level:2},{value:"Named scope lifetime",id:"named-scope-lifetime",level:2},{value:"Per-request lifetime",id:"per-request-lifetime",level:2},{value:"Per-scoped request lifetime",id:"per-scoped-request-lifetime",level:2},{value:"Auto lifetime",id:"auto-lifetime",level:2},{value:"Custom lifetime",id:"custom-lifetime",level:2}];function p(e){const i={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h1,{id:"lifetimes",children:"Lifetimes"}),"\n",(0,t.jsxs)(i.p,{children:["Lifetime management controls how long a service's instances will live (from instantiation to ",(0,t.jsx)(i.a,{href:"/docs/guides/scopes#disposal",children:"disposal"}),") and how they will be reused between resolution requests."]}),"\n",(0,t.jsx)(i.admonition,{type:"info",children:(0,t.jsxs)(i.p,{children:["Choosing the right lifetime helps you avoid ",(0,t.jsx)(i.a,{href:"/docs/diagnostics/validation#lifetime-validation",children:"captive dependencies"}),"."]})}),"\n",(0,t.jsx)(i.h2,{id:"default-lifetime",children:"Default lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(i.p,{children:["When you are not specifying a lifetime during registration, Stashbox will use the default lifetime. By default, it's set to ",(0,t.jsx)(i.a,{href:"#transient-lifetime",children:"Transient"}),", but you can override it with the ",(0,t.jsx)(i.code,{children:".WithDefaultLifetime()"})," ",(0,t.jsx)(i.a,{href:"/docs/configuration/container-configuration#default-lifetime",children:"container configuration option"}),"."]}),(0,t.jsxs)(i.p,{children:["You can choose either from the pre-defined lifetimes defined on the ",(0,t.jsx)(i.code,{children:"Lifetimes"})," static class or use a ",(0,t.jsx)(i.a,{href:"#custom-lifetime",children:"custom lifetime"}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Transient (default)",label:"Transient (default)",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"var container = new StashboxContainer(options => options\n .WithDefaultLifetime(Lifetimes.Transient));\n"})})}),(0,t.jsx)(a.A,{value:"Singleton",label:"Singleton",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"var container = new StashboxContainer(options => options\n .WithDefaultLifetime(Lifetimes.Singleton));\n"})})}),(0,t.jsx)(a.A,{value:"Scoped",label:"Scoped",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"var container = new StashboxContainer(options => options\n .WithDefaultLifetime(Lifetimes.Scoped));\n"})})})]})})]}),"\n",(0,t.jsx)(i.h2,{id:"transient-lifetime",children:"Transient lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(i.p,{children:["A new instance is created for each resolution request. If a transient is referred by multiple consumers in the same ",(0,t.jsx)(i.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"}),", each will get a new instance."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithLifetime(Lifetimes.Transient));\n"})})})]}),"\n",(0,t.jsx)(i.admonition,{type:"info",children:(0,t.jsxs)(i.p,{children:["Transient services are not tracked for disposal by default, but this feature can be turned on with the ",(0,t.jsx)(i.code,{children:".WithDisposableTransientTracking()"})," ",(0,t.jsx)(i.a,{href:"/docs/configuration/container-configuration#tracking-disposable-transients",children:"container configuration option"}),". When it's enabled, the current ",(0,t.jsx)(i.a,{href:"/docs/guides/scopes",children:"scope"})," on which the resolution request was initiated takes the responsibility to track and dispose transient services."]})}),"\n",(0,t.jsx)(i.h2,{id:"singleton-lifetime",children:"Singleton lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(i.p,{children:"A single instance is created and reused for each resolution request and injected into each consumer."}),(0,t.jsx)(i.admonition,{type:"note",children:(0,t.jsxs)(i.p,{children:["Singleton services are disposed when the container (",(0,t.jsx)(i.a,{href:"/docs/getting-started/glossary#root-scope",children:"root scope"}),") is being disposed."]})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{groupId:"lifetime-forms",children:[(0,t.jsx)(a.A,{value:"Longer form",label:"Longer form",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithLifetime(Lifetimes.Singleton));\n"})})}),(0,t.jsx)(a.A,{value:"Shorter form",label:"Shorter form",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.RegisterSingleton();\n"})})})]})})]}),"\n",(0,t.jsx)(i.h2,{id:"scoped-lifetime",children:"Scoped lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(i.p,{children:["A new instance is created for each ",(0,t.jsx)(i.a,{href:"/docs/guides/scopes",children:"scope"}),", which will be returned for every resolution request initiated on the given scope. It's like a singleton lifetime within a scope."]}),(0,t.jsx)(i.admonition,{type:"note",children:(0,t.jsx)(i.p,{children:"Scoped services are disposed when their scope is being disposed."})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{groupId:"lifetime-forms",children:[(0,t.jsx)(a.A,{value:"Longer form",label:"Longer form",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithLifetime(Lifetimes.Scoped));\n\nusing var scope = container.BeginScope();\nIJob job = scope.Resolve();\n"})})}),(0,t.jsx)(a.A,{value:"Shorter form",label:"Shorter form",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.RegisterScoped();\n\nusing var scope = container.BeginScope();\nIJob job = scope.Resolve();\n"})})})]})})]}),"\n",(0,t.jsx)(i.h2,{id:"named-scope-lifetime",children:"Named scope lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(i.p,{children:"It is the same as scoped lifetime, except the given service will be selected only when a scope with the same name initiates the resolution request."}),(0,t.jsxs)(i.p,{children:["You can also let a service ",(0,t.jsx)(i.a,{href:"/docs/guides/scopes#service-as-scope",children:"define"})," its own named scope. During registration, this scope can be referred to by its name upon using a named scope lifetime."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Named",label:"Named",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .InNamedScope("DbScope"));\n\nusing var scope = container.BeginScope("DbScope");\nIJob job = scope.Resolve();\n'})})}),(0,t.jsx)(a.A,{value:"Defined",label:"Defined",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .DefinesScope());\n\nontainer.Register(options => options\n .InScopeDefinedBy());\n\n// the executor will begin a new scope within itself\n// when it gets resolved and DbBackup will be selected\n// and attached to that scope instead.\nusing var scope = container.BeginScope();\nDbJobExecutor executor = scope.Resolve();\n"})})}),(0,t.jsx)(a.A,{value:"Defined with name",label:"Defined with name",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .DefinesScope("DbScope"));\n\nontainer.Register(options => options\n .InNamedScope("DbScope"));\n\n// the executor will begin a new scope within itself\n// when it gets resolved and DbBackup will be selected\n// and attached to that scope instead.\nusing var scope = container.BeginScope();\nDbJobExecutor executor = scope.Resolve();\n'})})})]})})]}),"\n",(0,t.jsx)(i.admonition,{type:"note",children:(0,t.jsx)(i.p,{children:"Services with named scope lifetime are disposed when the related named scope is being disposed."})}),"\n",(0,t.jsx)(i.h2,{id:"per-request-lifetime",children:"Per-request lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(i.p,{children:"The requested service will be reused within the whole resolution request. A new instance is created for each individual request ."})}),(0,t.jsx)("div",{children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithPerRequestLifetime());\n"})})})]}),"\n",(0,t.jsx)(i.h2,{id:"per-scoped-request-lifetime",children:"Per-scoped request lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(i.p,{children:"The requested service will behave like a singleton, but only within a scoped dependency request. This means every scoped service will get a new exclusive instance that will be used by its sub-dependencies as well."})}),(0,t.jsx)("div",{children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithPerScopedRequestLifetime());\n"})})})]}),"\n",(0,t.jsx)(i.h2,{id:"auto-lifetime",children:"Auto lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(i.p,{children:"The requested service's lifetime will align to the lifetime of its dependencies. When the requested service has a dependency with a higher lifespan, this lifetime will inherit that lifespan up to a given boundary."})}),(0,t.jsx)("div",{children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(i.p,{children:"If the requested service has auto lifetime with a scoped boundary and it has only transient dependencies, it'll inherit their transient lifetime."})}),(0,t.jsx)("div",{children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register();\n\ncontainer.Register(options => options\n .WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));\n\n// job has transient lifetime.\nvar job = container.Resolve();\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(i.p,{children:"When there's a dependency with higher lifespan than the given boundary, the requested service will get the boundary lifetime."})}),(0,t.jsx)("div",{children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.RegisterSingleton();\n\ncontainer.Register(options => options\n .WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));\n\n// job has scoped lifetime.\nvar job = container.Resolve();\n"})})})]}),"\n",(0,t.jsx)(i.h2,{id:"custom-lifetime",children:"Custom lifetime"}),"\n",(0,t.jsxs)(i.p,{children:["If you'd like to use a custom lifetime, you can create your implementation by inheriting either from ",(0,t.jsx)(i.code,{children:"FactoryLifetimeDescriptor"})," or from ",(0,t.jsx)(i.code,{children:"ExpressionLifetimeDescriptor"}),", depending on how do you want to manage the service instances."]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:["\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.strong,{children:"ExpressionLifetimeDescriptor"}),": With this, you can build your lifetime with the expression form of the service instantiation."]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"class CustomLifetime : ExpressionLifetimeDescriptor\n{\n protected override Expression ApplyLifetime(\n Expression expression, // The expression which describes the service creation\n ServiceRegistration serviceRegistration, \n ResolutionContext resolutionContext, \n Type requestedType)\n {\n // Lifetime managing functionality\n }\n}\n"})}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:["\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.strong,{children:"FactoryLifetimeDescriptor"}),": With this, you can build your lifetime based on a pre-compiled factory delegate used for service instantiation."]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"class CustomLifetime : FactoryLifetimeDescriptor\n{\n protected override Expression ApplyLifetime(\n Func factory, // The factory used for service creation\n ServiceRegistration serviceRegistration, \n ResolutionContext resolutionContext, \n Type requestedType)\n {\n // Lifetime managing functionality\n }\n}\n"})}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(i.p,{children:"Then you can use your lifetime like this:"}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options.WithLifetime(new CustomLifetime()));\n"})})]})}function f(e={}){const{wrapper:i}={...(0,s.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(p,{...e})}):p(e)}},9365:(e,i,n)=>{n.d(i,{A:()=>o});n(6540);var t=n(870);const s={tabItem:"tabItem_Ymn6"};var r=n(4848);function o(e){let{children:i,hidden:n,className:o}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,t.A)(s.tabItem,o),hidden:n,children:i})}},1470:(e,i,n)=>{n.d(i,{A:()=>y});var t=n(6540),s=n(870),r=n(3104),o=n(6347),a=n(205),l=n(7485),c=n(1682),d=n(9466);function u(e){return t.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,t.isValidElement)(e)&&function(e){const{props:i}=e;return!!i&&"object"==typeof i&&"value"in i}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:i,children:n}=e;return(0,t.useMemo)((()=>{const e=i??function(e){return u(e).map((e=>{let{props:{value:i,label:n,attributes:t,default:s}}=e;return{value:i,label:n,attributes:t,default:s}}))}(n);return function(e){const i=(0,c.X)(e,((e,i)=>e.value===i.value));if(i.length>0)throw new Error(`Docusaurus error: Duplicate values "${i.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[i,n])}function p(e){let{value:i,tabValues:n}=e;return n.some((e=>e.value===i))}function f(e){let{queryString:i=!1,groupId:n}=e;const s=(0,o.W6)(),r=function(e){let{queryString:i=!1,groupId:n}=e;if("string"==typeof i)return i;if(!1===i)return null;if(!0===i&&!n)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:i,groupId:n});return[(0,l.aZ)(r),(0,t.useCallback)((e=>{if(!r)return;const i=new URLSearchParams(s.location.search);i.set(r,e),s.replace({...s.location,search:i.toString()})}),[r,s])]}function m(e){const{defaultValue:i,queryString:n=!1,groupId:s}=e,r=h(e),[o,l]=(0,t.useState)((()=>function(e){let{defaultValue:i,tabValues:n}=e;if(0===n.length)throw new Error("Docusaurus error: the component requires at least one children component");if(i){if(!p({value:i,tabValues:n}))throw new Error(`Docusaurus error: The has a defaultValue "${i}" but none of its children has the corresponding value. Available values are: ${n.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return i}const t=n.find((e=>e.default))??n[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:i,tabValues:r}))),[c,u]=f({queryString:n,groupId:s}),[m,x]=function(e){let{groupId:i}=e;const n=function(e){return e?`docusaurus.tab.${e}`:null}(i),[s,r]=(0,d.Dv)(n);return[s,(0,t.useCallback)((e=>{n&&r.set(e)}),[n,r])]}({groupId:s}),g=(()=>{const e=c??m;return p({value:e,tabValues:r})?e:null})();(0,a.A)((()=>{g&&l(g)}),[g]);return{selectedValue:o,selectValue:(0,t.useCallback)((e=>{if(!p({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);l(e),u(e),x(e)}),[u,x,r]),tabValues:r}}var x=n(2303);const g={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var b=n(4848);function j(e){let{className:i,block:n,selectedValue:t,selectValue:o,tabValues:a}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,r.a_)(),d=e=>{const i=e.currentTarget,n=l.indexOf(i),s=a[n].value;s!==t&&(c(i),o(s))},u=e=>{let i=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const n=l.indexOf(e.currentTarget)+1;i=l[n]??l[0];break}case"ArrowLeft":{const n=l.indexOf(e.currentTarget)-1;i=l[n]??l[l.length-1];break}}i?.focus()};return(0,b.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":n},i),children:a.map((e=>{let{value:i,label:n,attributes:r}=e;return(0,b.jsx)("li",{role:"tab",tabIndex:t===i?0:-1,"aria-selected":t===i,ref:e=>l.push(e),onKeyDown:u,onClick:d,...r,className:(0,s.A)("tabs__item",g.tabItem,r?.className,{"tabs__item--active":t===i}),children:n??i},i)}))})}function v(e){let{lazy:i,children:n,selectedValue:s}=e;const r=(Array.isArray(n)?n:[n]).filter(Boolean);if(i){const e=r.find((e=>e.props.value===s));return e?(0,t.cloneElement)(e,{className:"margin-top--md"}):null}return(0,b.jsx)("div",{className:"margin-top--md",children:r.map(((e,i)=>(0,t.cloneElement)(e,{key:i,hidden:e.props.value!==s})))})}function w(e){const i=m(e);return(0,b.jsxs)("div",{className:(0,s.A)("tabs-container",g.tabList),children:[(0,b.jsx)(j,{...e,...i}),(0,b.jsx)(v,{...e,...i})]})}function y(e){const i=(0,x.A)();return(0,b.jsx)(w,{...e,children:u(e.children)},String(i))}},7470:(e,i,n)=>{n.d(i,{A:()=>o});var t=n(6540);const s={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var r=n(4848);function o(e){let{children:i}=e,n=t.Children.toArray(i).filter((e=>e));return(0,r.jsxs)("div",{className:s.codeDescContainer,children:[(0,r.jsx)("div",{className:s.desc,children:n[0]}),(0,r.jsx)("div",{className:s.example,children:n[1]})]})}},8453:(e,i,n)=>{n.d(i,{R:()=>o,x:()=>a});var t=n(6540);const s={},r=t.createContext(s);function o(e){const i=t.useContext(r);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function a(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),t.createElement(r.Provider,{value:i},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[338],{8594:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>u,contentTitle:()=>c,default:()=>f,frontMatter:()=>l,metadata:()=>d,toc:()=>h});var t=n(4848),s=n(8453),r=n(7470),o=n(1470),a=n(9365);const l={},c="Lifetimes",d={id:"guides/lifetimes",title:"Lifetimes",description:"Lifetime management controls how long a service's instances will live (from instantiation to disposal) and how they will be reused between resolution requests.",source:"@site/docs/guides/lifetimes.md",sourceDirName:"guides",slug:"/guides/lifetimes",permalink:"/stashbox/docs/guides/lifetimes",draft:!1,unlisted:!1,editUrl:"https://github.com/z4kn4fein/stashbox/edit/master/docs/docs/guides/lifetimes.md",tags:[],version:"current",lastUpdatedBy:"dependabot[bot]",lastUpdatedAt:1734176984,formattedLastUpdatedAt:"Dec 14, 2024",frontMatter:{},sidebar:"docs",previous:{title:"Service resolution",permalink:"/stashbox/docs/guides/service-resolution"},next:{title:"Scopes",permalink:"/stashbox/docs/guides/scopes"}},u={},h=[{value:"Default lifetime",id:"default-lifetime",level:2},{value:"Transient lifetime",id:"transient-lifetime",level:2},{value:"Singleton lifetime",id:"singleton-lifetime",level:2},{value:"Scoped lifetime",id:"scoped-lifetime",level:2},{value:"Named scope lifetime",id:"named-scope-lifetime",level:2},{value:"Per-request lifetime",id:"per-request-lifetime",level:2},{value:"Per-scoped request lifetime",id:"per-scoped-request-lifetime",level:2},{value:"Auto lifetime",id:"auto-lifetime",level:2},{value:"Custom lifetime",id:"custom-lifetime",level:2}];function p(e){const i={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.h1,{id:"lifetimes",children:"Lifetimes"}),"\n",(0,t.jsxs)(i.p,{children:["Lifetime management controls how long a service's instances will live (from instantiation to ",(0,t.jsx)(i.a,{href:"/docs/guides/scopes#disposal",children:"disposal"}),") and how they will be reused between resolution requests."]}),"\n",(0,t.jsx)(i.admonition,{type:"info",children:(0,t.jsxs)(i.p,{children:["Choosing the right lifetime helps you avoid ",(0,t.jsx)(i.a,{href:"/docs/diagnostics/validation#lifetime-validation",children:"captive dependencies"}),"."]})}),"\n",(0,t.jsx)(i.h2,{id:"default-lifetime",children:"Default lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(i.p,{children:["When you are not specifying a lifetime during registration, Stashbox will use the default lifetime. By default, it's set to ",(0,t.jsx)(i.a,{href:"#transient-lifetime",children:"Transient"}),", but you can override it with the ",(0,t.jsx)(i.code,{children:".WithDefaultLifetime()"})," ",(0,t.jsx)(i.a,{href:"/docs/configuration/container-configuration#default-lifetime",children:"container configuration option"}),"."]}),(0,t.jsxs)(i.p,{children:["You can choose either from the pre-defined lifetimes defined on the ",(0,t.jsx)(i.code,{children:"Lifetimes"})," static class or use a ",(0,t.jsx)(i.a,{href:"#custom-lifetime",children:"custom lifetime"}),"."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Transient (default)",label:"Transient (default)",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"var container = new StashboxContainer(options => options\n .WithDefaultLifetime(Lifetimes.Transient));\n"})})}),(0,t.jsx)(a.A,{value:"Singleton",label:"Singleton",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"var container = new StashboxContainer(options => options\n .WithDefaultLifetime(Lifetimes.Singleton));\n"})})}),(0,t.jsx)(a.A,{value:"Scoped",label:"Scoped",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"var container = new StashboxContainer(options => options\n .WithDefaultLifetime(Lifetimes.Scoped));\n"})})})]})})]}),"\n",(0,t.jsx)(i.h2,{id:"transient-lifetime",children:"Transient lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsxs)(i.p,{children:["A new instance is created for each resolution request. If a transient is referred by multiple consumers in the same ",(0,t.jsx)(i.a,{href:"/docs/getting-started/glossary#resolution-tree",children:"resolution tree"}),", each will get a new instance."]})}),(0,t.jsx)("div",{children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithLifetime(Lifetimes.Transient));\n"})})})]}),"\n",(0,t.jsx)(i.admonition,{type:"info",children:(0,t.jsxs)(i.p,{children:["Transient services are not tracked for disposal by default, but this feature can be turned on with the ",(0,t.jsx)(i.code,{children:".WithDisposableTransientTracking()"})," ",(0,t.jsx)(i.a,{href:"/docs/configuration/container-configuration#tracking-disposable-transients",children:"container configuration option"}),". When it's enabled, the current ",(0,t.jsx)(i.a,{href:"/docs/guides/scopes",children:"scope"})," on which the resolution request was initiated takes the responsibility to track and dispose transient services."]})}),"\n",(0,t.jsx)(i.h2,{id:"singleton-lifetime",children:"Singleton lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(i.p,{children:"A single instance is created and reused for each resolution request and injected into each consumer."}),(0,t.jsx)(i.admonition,{type:"note",children:(0,t.jsxs)(i.p,{children:["Singleton services are disposed when the container (",(0,t.jsx)(i.a,{href:"/docs/getting-started/glossary#root-scope",children:"root scope"}),") is being disposed."]})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{groupId:"lifetime-forms",children:[(0,t.jsx)(a.A,{value:"Longer form",label:"Longer form",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithLifetime(Lifetimes.Singleton));\n"})})}),(0,t.jsx)(a.A,{value:"Shorter form",label:"Shorter form",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.RegisterSingleton();\n"})})})]})})]}),"\n",(0,t.jsx)(i.h2,{id:"scoped-lifetime",children:"Scoped lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(i.p,{children:["A new instance is created for each ",(0,t.jsx)(i.a,{href:"/docs/guides/scopes",children:"scope"}),", which will be returned for every resolution request initiated on the given scope. It's like a singleton lifetime within a scope."]}),(0,t.jsx)(i.admonition,{type:"note",children:(0,t.jsx)(i.p,{children:"Scoped services are disposed when their scope is being disposed."})})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{groupId:"lifetime-forms",children:[(0,t.jsx)(a.A,{value:"Longer form",label:"Longer form",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithLifetime(Lifetimes.Scoped));\n\nusing var scope = container.BeginScope();\nIJob job = scope.Resolve();\n"})})}),(0,t.jsx)(a.A,{value:"Shorter form",label:"Shorter form",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.RegisterScoped();\n\nusing var scope = container.BeginScope();\nIJob job = scope.Resolve();\n"})})})]})})]}),"\n",(0,t.jsx)(i.h2,{id:"named-scope-lifetime",children:"Named scope lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsxs)("div",{children:[(0,t.jsx)(i.p,{children:"It is the same as scoped lifetime, except the given service will be selected only when a scope with the same name initiates the resolution request."}),(0,t.jsxs)(i.p,{children:["You can also let a service ",(0,t.jsx)(i.a,{href:"/docs/guides/scopes#service-as-scope",children:"define"})," its own named scope. During registration, this scope can be referred to by its name upon using a named scope lifetime."]})]}),(0,t.jsx)("div",{children:(0,t.jsxs)(o.A,{children:[(0,t.jsx)(a.A,{value:"Named",label:"Named",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .InNamedScope("DbScope"));\n\nusing var scope = container.BeginScope("DbScope");\nIJob job = scope.Resolve();\n'})})}),(0,t.jsx)(a.A,{value:"Defined",label:"Defined",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .DefinesScope());\n\nontainer.Register(options => options\n .InScopeDefinedBy());\n\n// the executor will begin a new scope within itself\n// when it gets resolved and DbBackup will be selected\n// and attached to that scope instead.\nusing var scope = container.BeginScope();\nDbJobExecutor executor = scope.Resolve();\n"})})}),(0,t.jsx)(a.A,{value:"Defined with name",label:"Defined with name",children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:'container.Register(options => options\n .DefinesScope("DbScope"));\n\nontainer.Register(options => options\n .InNamedScope("DbScope"));\n\n// the executor will begin a new scope within itself\n// when it gets resolved and DbBackup will be selected\n// and attached to that scope instead.\nusing var scope = container.BeginScope();\nDbJobExecutor executor = scope.Resolve();\n'})})})]})})]}),"\n",(0,t.jsx)(i.admonition,{type:"note",children:(0,t.jsx)(i.p,{children:"Services with named scope lifetime are disposed when the related named scope is being disposed."})}),"\n",(0,t.jsx)(i.h2,{id:"per-request-lifetime",children:"Per-request lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(i.p,{children:"The requested service will be reused within the whole resolution request. A new instance is created for each individual request ."})}),(0,t.jsx)("div",{children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithPerRequestLifetime());\n"})})})]}),"\n",(0,t.jsx)(i.h2,{id:"per-scoped-request-lifetime",children:"Per-scoped request lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(i.p,{children:"The requested service will behave like a singleton, but only within a scoped dependency request. This means every scoped service will get a new exclusive instance that will be used by its sub-dependencies as well."})}),(0,t.jsx)("div",{children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithPerScopedRequestLifetime());\n"})})})]}),"\n",(0,t.jsx)(i.h2,{id:"auto-lifetime",children:"Auto lifetime"}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(i.p,{children:"The requested service's lifetime will align to the lifetime of its dependencies. When the requested service has a dependency with a higher lifespan, this lifetime will inherit that lifespan up to a given boundary."})}),(0,t.jsx)("div",{children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options\n .WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(i.p,{children:"If the requested service has auto lifetime with a scoped boundary and it has only transient dependencies, it'll inherit their transient lifetime."})}),(0,t.jsx)("div",{children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register();\n\ncontainer.Register(options => options\n .WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));\n\n// job has transient lifetime.\nvar job = container.Resolve();\n"})})})]}),"\n",(0,t.jsxs)(r.A,{children:[(0,t.jsx)("div",{children:(0,t.jsx)(i.p,{children:"When there's a dependency with higher lifespan than the given boundary, the requested service will get the boundary lifetime."})}),(0,t.jsx)("div",{children:(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.RegisterSingleton();\n\ncontainer.Register(options => options\n .WithAutoLifetime(Lifetimes.Scoped /* boundary lifetime */));\n\n// job has scoped lifetime.\nvar job = container.Resolve();\n"})})})]}),"\n",(0,t.jsx)(i.h2,{id:"custom-lifetime",children:"Custom lifetime"}),"\n",(0,t.jsxs)(i.p,{children:["If you'd like to use a custom lifetime, you can create your implementation by inheriting either from ",(0,t.jsx)(i.code,{children:"FactoryLifetimeDescriptor"})," or from ",(0,t.jsx)(i.code,{children:"ExpressionLifetimeDescriptor"}),", depending on how do you want to manage the service instances."]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:["\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.strong,{children:"ExpressionLifetimeDescriptor"}),": With this, you can build your lifetime with the expression form of the service instantiation."]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"class CustomLifetime : ExpressionLifetimeDescriptor\n{\n protected override Expression ApplyLifetime(\n Expression expression, // The expression which describes the service creation\n ServiceRegistration serviceRegistration, \n ResolutionContext resolutionContext, \n Type requestedType)\n {\n // Lifetime managing functionality\n }\n}\n"})}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:["\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.strong,{children:"FactoryLifetimeDescriptor"}),": With this, you can build your lifetime based on a pre-compiled factory delegate used for service instantiation."]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"class CustomLifetime : FactoryLifetimeDescriptor\n{\n protected override Expression ApplyLifetime(\n Func factory, // The factory used for service creation\n ServiceRegistration serviceRegistration, \n ResolutionContext resolutionContext, \n Type requestedType)\n {\n // Lifetime managing functionality\n }\n}\n"})}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(i.p,{children:"Then you can use your lifetime like this:"}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-cs",children:"container.Register(options => options.WithLifetime(new CustomLifetime()));\n"})})]})}function f(e={}){const{wrapper:i}={...(0,s.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(p,{...e})}):p(e)}},9365:(e,i,n)=>{n.d(i,{A:()=>o});n(6540);var t=n(870);const s={tabItem:"tabItem_Ymn6"};var r=n(4848);function o(e){let{children:i,hidden:n,className:o}=e;return(0,r.jsx)("div",{role:"tabpanel",className:(0,t.A)(s.tabItem,o),hidden:n,children:i})}},1470:(e,i,n)=>{n.d(i,{A:()=>y});var t=n(6540),s=n(870),r=n(3104),o=n(6347),a=n(205),l=n(7485),c=n(1682),d=n(9466);function u(e){return t.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,t.isValidElement)(e)&&function(e){const{props:i}=e;return!!i&&"object"==typeof i&&"value"in i}(e))return e;throw new Error(`Docusaurus error: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:i,children:n}=e;return(0,t.useMemo)((()=>{const e=i??function(e){return u(e).map((e=>{let{props:{value:i,label:n,attributes:t,default:s}}=e;return{value:i,label:n,attributes:t,default:s}}))}(n);return function(e){const i=(0,c.X)(e,((e,i)=>e.value===i.value));if(i.length>0)throw new Error(`Docusaurus error: Duplicate values "${i.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[i,n])}function p(e){let{value:i,tabValues:n}=e;return n.some((e=>e.value===i))}function f(e){let{queryString:i=!1,groupId:n}=e;const s=(0,o.W6)(),r=function(e){let{queryString:i=!1,groupId:n}=e;if("string"==typeof i)return i;if(!1===i)return null;if(!0===i&&!n)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:i,groupId:n});return[(0,l.aZ)(r),(0,t.useCallback)((e=>{if(!r)return;const i=new URLSearchParams(s.location.search);i.set(r,e),s.replace({...s.location,search:i.toString()})}),[r,s])]}function m(e){const{defaultValue:i,queryString:n=!1,groupId:s}=e,r=h(e),[o,l]=(0,t.useState)((()=>function(e){let{defaultValue:i,tabValues:n}=e;if(0===n.length)throw new Error("Docusaurus error: the component requires at least one children component");if(i){if(!p({value:i,tabValues:n}))throw new Error(`Docusaurus error: The has a defaultValue "${i}" but none of its children has the corresponding value. Available values are: ${n.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return i}const t=n.find((e=>e.default))??n[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:i,tabValues:r}))),[c,u]=f({queryString:n,groupId:s}),[m,x]=function(e){let{groupId:i}=e;const n=function(e){return e?`docusaurus.tab.${e}`:null}(i),[s,r]=(0,d.Dv)(n);return[s,(0,t.useCallback)((e=>{n&&r.set(e)}),[n,r])]}({groupId:s}),g=(()=>{const e=c??m;return p({value:e,tabValues:r})?e:null})();(0,a.A)((()=>{g&&l(g)}),[g]);return{selectedValue:o,selectValue:(0,t.useCallback)((e=>{if(!p({value:e,tabValues:r}))throw new Error(`Can't select invalid tab value=${e}`);l(e),u(e),x(e)}),[u,x,r]),tabValues:r}}var x=n(2303);const g={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var b=n(4848);function j(e){let{className:i,block:n,selectedValue:t,selectValue:o,tabValues:a}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,r.a_)(),d=e=>{const i=e.currentTarget,n=l.indexOf(i),s=a[n].value;s!==t&&(c(i),o(s))},u=e=>{let i=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const n=l.indexOf(e.currentTarget)+1;i=l[n]??l[0];break}case"ArrowLeft":{const n=l.indexOf(e.currentTarget)-1;i=l[n]??l[l.length-1];break}}i?.focus()};return(0,b.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":n},i),children:a.map((e=>{let{value:i,label:n,attributes:r}=e;return(0,b.jsx)("li",{role:"tab",tabIndex:t===i?0:-1,"aria-selected":t===i,ref:e=>l.push(e),onKeyDown:u,onClick:d,...r,className:(0,s.A)("tabs__item",g.tabItem,r?.className,{"tabs__item--active":t===i}),children:n??i},i)}))})}function v(e){let{lazy:i,children:n,selectedValue:s}=e;const r=(Array.isArray(n)?n:[n]).filter(Boolean);if(i){const e=r.find((e=>e.props.value===s));return e?(0,t.cloneElement)(e,{className:"margin-top--md"}):null}return(0,b.jsx)("div",{className:"margin-top--md",children:r.map(((e,i)=>(0,t.cloneElement)(e,{key:i,hidden:e.props.value!==s})))})}function w(e){const i=m(e);return(0,b.jsxs)("div",{className:(0,s.A)("tabs-container",g.tabList),children:[(0,b.jsx)(j,{...e,...i}),(0,b.jsx)(v,{...e,...i})]})}function y(e){const i=(0,x.A)();return(0,b.jsx)(w,{...e,children:u(e.children)},String(i))}},7470:(e,i,n)=>{n.d(i,{A:()=>o});var t=n(6540);const s={codeDescContainer:"codeDescContainer_ie8f",desc:"desc_jyqI",example:"example_eYlF"};var r=n(4848);function o(e){let{children:i}=e,n=t.Children.toArray(i).filter((e=>e));return(0,r.jsxs)("div",{className:s.codeDescContainer,children:[(0,r.jsx)("div",{className:s.desc,children:n[0]}),(0,r.jsx)("div",{className:s.example,children:n[1]})]})}},8453:(e,i,n)=>{n.d(i,{R:()=>o,x:()=>a});var t=n(6540);const s={},r=t.createContext(s);function o(e){const i=t.useContext(r);return t.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function a(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),t.createElement(r.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.7f3eed93.js b/assets/js/runtime~main.7f3eed93.js new file mode 100644 index 00000000..bc3f17e4 --- /dev/null +++ b/assets/js/runtime~main.7f3eed93.js @@ -0,0 +1 @@ +(()=>{"use strict";var e,t,r,a,f,o={},d={};function n(e){var t=d[e];if(void 0!==t)return t.exports;var r=d[e]={exports:{}};return o[e].call(r.exports,r,r.exports,n),r.exports}n.m=o,e=[],n.O=(t,r,a,f)=>{if(!r){var o=1/0;for(b=0;b=f)&&Object.keys(n.O).every((e=>n.O[e](r[c])))?r.splice(c--,1):(d=!1,f0&&e[b-1][2]>f;b--)e[b]=e[b-1];e[b]=[r,a,f]},n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,n.t=function(e,a){if(1&a&&(e=this(e)),8&a)return e;if("object"==typeof e&&e){if(4&a&&e.__esModule)return e;if(16&a&&"function"==typeof e.then)return e}var f=Object.create(null);n.r(f);var o={};t=t||[null,r({}),r([]),r(r)];for(var d=2&a&&e;"object"==typeof d&&!~t.indexOf(d);d=r(d))Object.getOwnPropertyNames(d).forEach((t=>o[t]=()=>e[t]));return o.default=()=>e,n.d(f,o),f},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce(((t,r)=>(n.f[r](e,t),t)),[])),n.u=e=>"assets/js/"+({48:"a94703ab",84:"a0f36b27",98:"a7bd4aaa",109:"dd45a7f1",138:"1a4e3797",271:"7a96ca3d",273:"19b690e4",289:"9ff4038f",313:"026a065b",338:"ff10094c",371:"b53e7bc5",384:"b43f639d",401:"17896441",414:"4047545b",429:"61ac6d0c",494:"a1e0d343",495:"fd379919",545:"5b5f0b93",581:"935f2afb",634:"c4f5d8e4",647:"5e95c892",747:"6d4ed487",793:"115c3c33",799:"74fdf922",803:"3a87badd",878:"851a7a9e",893:"8a74683a"}[e]||e)+"."+{48:"1ef2a825",84:"6364979d",98:"f8045a62",109:"85193944",117:"4c138acf",138:"440b3f3b",237:"61b1d32a",271:"8d38ba06",273:"2c4ecbd1",289:"72dcabb1",313:"c4544f30",338:"ccd23fc1",371:"2ffd6d83",384:"e18adc97",401:"b426dd8a",414:"f98d0351",416:"36a683d5",429:"aa2e8309",462:"434a1547",494:"62369d9d",495:"6f340728",545:"23961e77",581:"9959f74a",634:"173c1c7d",647:"1bcffdfc",747:"af545583",793:"02b13161",799:"71931827",803:"706346b9",878:"c87840f5",893:"c5a01ec5",913:"f65583d1"}[e]+".js",n.miniCssF=e=>{},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a={},f="website:",n.l=(e,t,r,o)=>{if(a[e])a[e].push(t);else{var d,c;if(void 0!==r)for(var i=document.getElementsByTagName("script"),b=0;b{d.onerror=d.onload=null,clearTimeout(s);var f=a[e];if(delete a[e],d.parentNode&&d.parentNode.removeChild(d),f&&f.forEach((e=>e(r))),t)return t(r)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=l.bind(null,d.onerror),d.onload=l.bind(null,d.onload),c&&document.head.appendChild(d)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.p="/stashbox/",n.gca=function(e){return e={17896441:"401",a94703ab:"48",a0f36b27:"84",a7bd4aaa:"98",dd45a7f1:"109","1a4e3797":"138","7a96ca3d":"271","19b690e4":"273","9ff4038f":"289","026a065b":"313",ff10094c:"338",b53e7bc5:"371",b43f639d:"384","4047545b":"414","61ac6d0c":"429",a1e0d343:"494",fd379919:"495","5b5f0b93":"545","935f2afb":"581",c4f5d8e4:"634","5e95c892":"647","6d4ed487":"747","115c3c33":"793","74fdf922":"799","3a87badd":"803","851a7a9e":"878","8a74683a":"893"}[e]||e,n.p+n.u(e)},(()=>{var e={354:0,869:0};n.f.j=(t,r)=>{var a=n.o(e,t)?e[t]:void 0;if(0!==a)if(a)r.push(a[2]);else if(/^(354|869)$/.test(t))e[t]=0;else{var f=new Promise(((r,f)=>a=e[t]=[r,f]));r.push(a[2]=f);var o=n.p+n.u(t),d=new Error;n.l(o,(r=>{if(n.o(e,t)&&(0!==(a=e[t])&&(e[t]=void 0),a)){var f=r&&("load"===r.type?"missing":r.type),o=r&&r.target&&r.target.src;d.message="Loading chunk "+t+" failed.\n("+f+": "+o+")",d.name="ChunkLoadError",d.type=f,d.request=o,a[1](d)}}),"chunk-"+t,t)}},n.O.j=t=>0===e[t];var t=(t,r)=>{var a,f,o=r[0],d=r[1],c=r[2],i=0;if(o.some((t=>0!==e[t]))){for(a in d)n.o(d,a)&&(n.m[a]=d[a]);if(c)var b=c(n)}for(t&&t(r);i{"use strict";var e,t,a,r,o,d={},f={};function n(e){var t=f[e];if(void 0!==t)return t.exports;var a=f[e]={exports:{}};return d[e].call(a.exports,a,a.exports,n),a.exports}n.m=d,e=[],n.O=(t,a,r,o)=>{if(!a){var d=1/0;for(b=0;b=o)&&Object.keys(n.O).every((e=>n.O[e](a[c])))?a.splice(c--,1):(f=!1,o0&&e[b-1][2]>o;b--)e[b]=e[b-1];e[b]=[a,r,o]},n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},a=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,n.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var o=Object.create(null);n.r(o);var d={};t=t||[null,a({}),a([]),a(a)];for(var f=2&r&&e;"object"==typeof f&&!~t.indexOf(f);f=a(f))Object.getOwnPropertyNames(f).forEach((t=>d[t]=()=>e[t]));return d.default=()=>e,n.d(o,d),o},n.d=(e,t)=>{for(var a in t)n.o(t,a)&&!n.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce(((t,a)=>(n.f[a](e,t),t)),[])),n.u=e=>"assets/js/"+({48:"a94703ab",84:"a0f36b27",98:"a7bd4aaa",109:"dd45a7f1",138:"1a4e3797",271:"7a96ca3d",273:"19b690e4",289:"9ff4038f",313:"026a065b",338:"ff10094c",371:"b53e7bc5",384:"b43f639d",401:"17896441",414:"4047545b",429:"61ac6d0c",494:"a1e0d343",495:"fd379919",545:"5b5f0b93",581:"935f2afb",634:"c4f5d8e4",647:"5e95c892",747:"6d4ed487",793:"115c3c33",799:"74fdf922",803:"3a87badd",878:"851a7a9e",893:"8a74683a"}[e]||e)+"."+{48:"1ef2a825",84:"6364979d",98:"f8045a62",109:"115c8e6a",117:"4c138acf",138:"440b3f3b",237:"61b1d32a",271:"7f57d2b9",273:"2c4ecbd1",289:"0e0ce6cd",313:"ab3f0196",338:"2763c19f",371:"56104c16",384:"30eb1c65",401:"b426dd8a",414:"9ec870e0",416:"36a683d5",429:"f2525d28",462:"434a1547",494:"44070a57",495:"463a8430",545:"7ea32b58",581:"9959f74a",634:"173c1c7d",647:"1bcffdfc",747:"43c43c69",793:"0d0b2113",799:"0b9b5797",803:"50d04665",878:"4e8777b3",893:"c5a01ec5",913:"f65583d1"}[e]+".js",n.miniCssF=e=>{},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},o="website:",n.l=(e,t,a,d)=>{if(r[e])r[e].push(t);else{var f,c;if(void 0!==a)for(var i=document.getElementsByTagName("script"),b=0;b{f.onerror=f.onload=null,clearTimeout(s);var o=r[e];if(delete r[e],f.parentNode&&f.parentNode.removeChild(f),o&&o.forEach((e=>e(a))),t)return t(a)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:f}),12e4);f.onerror=l.bind(null,f.onerror),f.onload=l.bind(null,f.onload),c&&document.head.appendChild(f)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.p="/stashbox/",n.gca=function(e){return e={17896441:"401",a94703ab:"48",a0f36b27:"84",a7bd4aaa:"98",dd45a7f1:"109","1a4e3797":"138","7a96ca3d":"271","19b690e4":"273","9ff4038f":"289","026a065b":"313",ff10094c:"338",b53e7bc5:"371",b43f639d:"384","4047545b":"414","61ac6d0c":"429",a1e0d343:"494",fd379919:"495","5b5f0b93":"545","935f2afb":"581",c4f5d8e4:"634","5e95c892":"647","6d4ed487":"747","115c3c33":"793","74fdf922":"799","3a87badd":"803","851a7a9e":"878","8a74683a":"893"}[e]||e,n.p+n.u(e)},(()=>{var e={354:0,869:0};n.f.j=(t,a)=>{var r=n.o(e,t)?e[t]:void 0;if(0!==r)if(r)a.push(r[2]);else if(/^(354|869)$/.test(t))e[t]=0;else{var o=new Promise(((a,o)=>r=e[t]=[a,o]));a.push(r[2]=o);var d=n.p+n.u(t),f=new Error;n.l(d,(a=>{if(n.o(e,t)&&(0!==(r=e[t])&&(e[t]=void 0),r)){var o=a&&("load"===a.type?"missing":a.type),d=a&&a.target&&a.target.src;f.message="Loading chunk "+t+" failed.\n("+o+": "+d+")",f.name="ChunkLoadError",f.type=o,f.request=d,r[1](f)}}),"chunk-"+t,t)}},n.O.j=t=>0===e[t];var t=(t,a)=>{var r,o,d=a[0],f=a[1],c=a[2],i=0;if(d.some((t=>0!==e[t]))){for(r in f)n.o(f,r)&&(n.m[r]=f[r]);if(c)var b=c(n)}for(t&&t(a);i