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