From 75f362e89f8a594c72b6de1517090b6d4c61bd83 Mon Sep 17 00:00:00 2001 From: Nikolay Pianikov Date: Mon, 20 Jan 2025 12:55:39 +0300 Subject: [PATCH] Support for wildcard hints and performance improvements --- README.md | 59 +++++- readme/FooterTemplate.md | 50 +++++ ...ncannotresolve-regular-expression-hint.md} | 6 +- readme/oncannotresolve-wildcard-hint.md | 159 ++++++++++++++ ...dencyinjection-regular-expression-hint.md} | 6 +- readme/ondependencyinjection-wildcard-hint.md | 162 ++++++++++++++ ... onnewinstance-regular-expression-hint.md} | 15 +- readme/onnewinstance-wildcard-hint.md | 176 +++++++++++++++ src/Pure.DI.Core/Components/Api.g.cs | 200 ++++++++++++++++++ .../Core/ApiInvocationProcessor.cs | 2 +- src/Pure.DI.Core/Core/Attributes.cs | 2 +- src/Pure.DI.Core/Core/Code/BuildTools.cs | 37 +++- .../Core/Code/CompositionClassBuilder.cs | 4 +- .../Core/Code/DisposeMethodBuilder.cs | 8 +- .../Core/Code/FactoryCodeBuilder.cs | 4 +- src/Pure.DI.Core/Core/Code/FactoryRewriter.cs | 2 +- .../Core/Code/ResolverClassesBuilder.cs | 6 +- .../Core/Code/ResolverFieldsBuilder.cs | 2 +- .../Core/Code/StaticConstructorBuilder.cs | 4 +- src/Pure.DI.Core/Core/Code/TypeResolver.cs | 4 +- .../Core/DependencyGraphBuilder.cs | 65 +++--- .../Core/DependencyGraphValidator.cs | 24 ++- src/Pure.DI.Core/Core/Filter.cs | 42 +++- src/Pure.DI.Core/Core/GenericTypeArguments.cs | 8 +- src/Pure.DI.Core/Core/IFilter.cs | 4 +- src/Pure.DI.Core/Core/ISymbolNames.cs | 4 +- src/Pure.DI.Core/Core/IWildcardMatcher.cs | 8 +- src/Pure.DI.Core/Core/MetadataSyntaxWalker.cs | 2 +- src/Pure.DI.Core/Core/Names.cs | 16 +- src/Pure.DI.Core/Core/SetupsBuilder.cs | 4 +- src/Pure.DI.Core/Core/SymbolNames.cs | 12 +- src/Pure.DI.Core/Core/WildcardMatcher.cs | 38 ++-- ...otResolveRegularExpressionHintScenario.cs} | 4 +- .../OnCannotResolveWildcardHintScenario.cs | 85 ++++++++ ...InjectionRegularExpressionHintScenario.cs} | 4 +- ...DependencyInjectionWildcardHintScenario.cs | 88 ++++++++ ...wInstanceRegularExpressionHintScenario.cs} | 7 +- .../OnNewInstanceWildcardHintScenario.cs | 91 ++++++++ 38 files changed, 1280 insertions(+), 134 deletions(-) rename readme/{oncannotresolve-hint.md => oncannotresolve-regular-expression-hint.md} (94%) create mode 100644 readme/oncannotresolve-wildcard-hint.md rename readme/{ondependencyinjection-hint.md => ondependencyinjection-regular-expression-hint.md} (94%) create mode 100644 readme/ondependencyinjection-wildcard-hint.md rename readme/{onnewinstance-hint.md => onnewinstance-regular-expression-hint.md} (88%) create mode 100644 readme/onnewinstance-wildcard-hint.md rename tests/Pure.DI.UsageTests/Hints/{OnCannotResolveHintScenario.cs => OnCannotResolveRegularExpressionHintScenario.cs} (94%) create mode 100644 tests/Pure.DI.UsageTests/Hints/OnCannotResolveWildcardHintScenario.cs rename tests/Pure.DI.UsageTests/Hints/{OnDependencyInjectionHintScenario.cs => OnDependencyInjectionRegularExpressionHintScenario.cs} (94%) create mode 100644 tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionWildcardHintScenario.cs rename tests/Pure.DI.UsageTests/Hints/{OnNewInstanceHintScenario.cs => OnNewInstanceRegularExpressionHintScenario.cs} (89%) create mode 100644 tests/Pure.DI.UsageTests/Hints/OnNewInstanceWildcardHintScenario.cs diff --git a/README.md b/README.md index 85ee8e3e9..af7840909 100644 --- a/README.md +++ b/README.md @@ -274,9 +274,12 @@ dotnet run ### Hints - [Resolve hint](readme/resolve-hint.md) - [ThreadSafe hint](readme/threadsafe-hint.md) -- [OnDependencyInjection hint](readme/ondependencyinjection-hint.md) -- [OnCannotResolve hint](readme/oncannotresolve-hint.md) -- [OnNewInstance hint](readme/onnewinstance-hint.md) +- [OnDependencyInjection regular expression hint](readme/ondependencyinjection-regular-expression-hint.md) +- [OnDependencyInjection wildcard hint](readme/ondependencyinjection-wildcard-hint.md) +- [OnCannotResolve regular expression hint](readme/oncannotresolve-regular-expression-hint.md) +- [OnCannotResolve wildcard hint](readme/oncannotresolve-wildcard-hint.md) +- [OnNewInstance regular expression hint](readme/onnewinstance-regular-expression-hint.md) +- [OnNewInstance wildcard hint](readme/onnewinstance-wildcard-hint.md) - [ToString hint](readme/tostring-hint.md) - [Check for a root](readme/check-for-a-root.md) ### Advanced @@ -590,19 +593,29 @@ DI.Setup("Composition") | [OnNewInstance](#onnewinstance-hint) | _On_ or _Off_ | 9.0 | _Off_ | | [OnNewInstancePartial](#onnewinstance-hint) | _On_ or _Off_ | | _On_ | | [OnNewInstanceImplementationTypeNameRegularExpression](#onnewinstanceimplementationtypenameregularexpression-hint) | Regular expression | | .+ | +| [OnNewInstanceImplementationTypeNameWildcard](#onnewinstanceimplementationtypenamewildcard-hint) | Wildcard | | * | | [OnNewInstanceTagRegularExpression](#onnewinstancetagregularexpression-hint) | Regular expression | | .+ | +| [OnNewInstanceTagWildcard](#onnewinstancetagwildcard-hint) | Wildcard | | * | | [OnNewInstanceLifetimeRegularExpression](#onnewinstancelifetimeregularexpression-hint) | Regular expression | | .+ | +| [OnNewInstanceLifetimeWildcard](#onnewinstancelifetimewildcard-hint) | Wildcard | | * | | [OnDependencyInjection](#ondependencyinjection-hint) | _On_ or _Off_ | 9.0 | _Off_ | | [OnDependencyInjectionPartial](#ondependencyinjectionpartial-hint) | _On_ or _Off_ | | _On_ | | [OnDependencyInjectionImplementationTypeNameRegularExpression](#OnDependencyInjectionImplementationTypeNameRegularExpression-Hint) | Regular expression | | .+ | +| [OnDependencyInjectionImplementationTypeNameWildcard](#OnDependencyInjectionImplementationTypeNameWildcard-Hint) | Wildcard | | * | | [OnDependencyInjectionContractTypeNameRegularExpression](#ondependencyinjectioncontracttypenameregularexpression-hint) | Regular expression | | .+ | +| [OnDependencyInjectionContractTypeNameWildcard](#ondependencyinjectioncontracttypenameWildcard-hint) | Wildcard | | * | | [OnDependencyInjectionTagRegularExpression](#ondependencyinjectiontagregularexpression-hint) | Regular expression | | .+ | +| [OnDependencyInjectionTagWildcard](#ondependencyinjectiontagWildcard-hint) | Wildcard | | * | | [OnDependencyInjectionLifetimeRegularExpression](#ondependencyinjectionlifetimeregularexpression-hint) | Regular expression | | .+ | +| [OnDependencyInjectionLifetimeWildcard](#ondependencyinjectionlifetimeWildcard-hint) | Wildcard | | * | | [OnCannotResolve](#oncannotresolve-hint) | _On_ or _Off_ | 9.0 | _Off_ | | [OnCannotResolvePartial](#oncannotresolvepartial-hint) | _On_ or _Off_ | | _On_ | | [OnCannotResolveContractTypeNameRegularExpression](#oncannotresolvecontracttypenameregularexpression-hint) | Regular expression | | .+ | +| [OnCannotResolveContractTypeNameWildcard](#oncannotresolvecontracttypenameцildcard-hint) | Wildcard | | * | | [OnCannotResolveTagRegularExpression](#oncannotresolvetagregularexpression-hint) | Regular expression | | .+ | +| [OnCannotResolveTagWildcard](#oncannotresolvetagWildcard-hint) | Wildcard | | * | | [OnCannotResolveLifetimeRegularExpression](#oncannotresolvelifetimeregularexpression-hint) | Regular expression | | .+ | +| [OnCannotResolveLifetimeWildcard](#oncannotresolvelifetimeWildcard-hint) | Wildcard | | * | | [OnNewRoot](#onnewroot-hint) | _On_ or _Off_ | | _Off_ | | [OnNewRootPartial](#onnewrootpartial-hint) | _On_ or _Off_ | | _On_ | | [ToString](#tostring-hint) | _On_ or _Off_ | | _Off_ | @@ -650,14 +663,26 @@ Determines whether to generate the _OnNewInstance_ partial method. By default, t This is a regular expression for filtering by instance type name. This hint is useful when _OnNewInstance_ is in _On_ state and it is necessary to limit the set of types for which the _OnNewInstance_ method will be called. +### OnNewInstanceImplementationTypeNameWildcard Hint + +This is a Wildcard for filtering by instance type name. This hint is useful when _OnNewInstance_ is in _On_ state and it is necessary to limit the set of types for which the _OnNewInstance_ method will be called. + ### OnNewInstanceTagRegularExpression Hint This is a regular expression for filtering by _tag_. This hint is also useful when _OnNewInstance_ is in _On_ state and it is necessary to limit the set of _tags_ for which the _OnNewInstance_ method will be called. +### OnNewInstanceTagWildcard Hint + +This is a wildcard for filtering by _tag_. This hint is also useful when _OnNewInstance_ is in _On_ state and it is necessary to limit the set of _tags_ for which the _OnNewInstance_ method will be called. + ### OnNewInstanceLifetimeRegularExpression Hint This is a regular expression for filtering by _lifetime_. This hint is also useful when _OnNewInstance_ is in _On_ state and it is necessary to restrict the set of _life_ times for which the _OnNewInstance_ method will be called. +### OnNewInstanceLifetimeWildcard Hint + +This is a wildcard for filtering by _lifetime_. This hint is also useful when _OnNewInstance_ is in _On_ state and it is necessary to restrict the set of _life_ times for which the _OnNewInstance_ method will be called. + ### OnDependencyInjection Hint Determines whether to use the _OnDependencyInjection_ partial method when the _OnDependencyInjection_ hint is ```On``` to control dependency injection. By default it is ```On```. @@ -689,18 +714,34 @@ To minimize performance loss when calling _OnDependencyInjection_, use the three This is a regular expression for filtering by instance type name. This hint is useful when _OnDependencyInjection_ is in _On_ state and it is necessary to restrict the set of types for which the _OnDependencyInjection_ method will be called. +### OnDependencyInjectionImplementationTypeNameWildcard Hint + +This is a wildcard for filtering by instance type name. This hint is useful when _OnDependencyInjection_ is in _On_ state and it is necessary to restrict the set of types for which the _OnDependencyInjection_ method will be called. + ### OnDependencyInjectionContractTypeNameRegularExpression Hint This is a regular expression for filtering by the name of the resolving type. This hint is also useful when _OnDependencyInjection_ is in _On_ state and it is necessary to limit the set of permissive types for which the _OnDependencyInjection_ method will be called. +### OnDependencyInjectionContractTypeNameWildcard Hint + +This is a wildcard for filtering by the name of the resolving type. This hint is also useful when _OnDependencyInjection_ is in _On_ state and it is necessary to limit the set of permissive types for which the _OnDependencyInjection_ method will be called. + ### OnDependencyInjectionTagRegularExpression Hint This is a regular expression for filtering by _tag_. This hint is also useful when _OnDependencyInjection_ is in the _On_ state and you want to limit the set of _tags_ for which the _OnDependencyInjection_ method will be called. +### OnDependencyInjectionTagWildcard Hint + +This is a wildcard for filtering by _tag_. This hint is also useful when _OnDependencyInjection_ is in the _On_ state and you want to limit the set of _tags_ for which the _OnDependencyInjection_ method will be called. + ### OnDependencyInjectionLifetimeRegularExpression Hint This is a regular expression for filtering by _lifetime_. This hint is also useful when _OnDependencyInjection_ is in _On_ state and it is necessary to restrict the set of _lifetime_ for which the _OnDependencyInjection_ method will be called. +### OnDependencyInjectionLifetimeWildcard Hint + +This is a wildcard for filtering by _lifetime_. This hint is also useful when _OnDependencyInjection_ is in _On_ state and it is necessary to restrict the set of _lifetime_ for which the _OnDependencyInjection_ method will be called. + ### OnCannotResolve Hint Determines whether to use the `OnCannotResolve(...)` partial method to handle a scenario in which an instance cannot be resolved. By default, this partial method is not generated. Because of the return value, it cannot have an empty body and must be overridden at creation. @@ -756,14 +797,26 @@ DI.Setup("Composition") This is a regular expression for filtering by the name of the resolving type. This hint is also useful when _OnCannotResolve_ is in _On_ state and it is necessary to limit the set of resolving types for which the _OnCannotResolve_ method will be called. +### OnCannotResolveContractTypeNameWildcard Hint + +This is a wildcard for filtering by the name of the resolving type. This hint is also useful when _OnCannotResolve_ is in _On_ state and it is necessary to limit the set of resolving types for which the _OnCannotResolve_ method will be called. + ### OnCannotResolveTagRegularExpression Hint This is a regular expression for filtering by _tag_. This hint is also useful when _OnCannotResolve_ is in _On_ state and it is necessary to limit the set of _tags_ for which the _OnCannotResolve_ method will be called. +### OnCannotResolveTagWildcard Hint + +This is a wildcard for filtering by _tag_. This hint is also useful when _OnCannotResolve_ is in _On_ state and it is necessary to limit the set of _tags_ for which the _OnCannotResolve_ method will be called. + ### OnCannotResolveLifetimeRegularExpression Hint This is a regular expression for filtering by _lifetime_. This hint is also useful when _OnCannotResolve_ is in the _On_ state and it is necessary to restrict the set of _lives_ for which the _OnCannotResolve_ method will be called. +### OnCannotResolveLifetimeWildcard Hint + +This is a wildcard for filtering by _lifetime_. This hint is also useful when _OnCannotResolve_ is in the _On_ state and it is necessary to restrict the set of _lives_ for which the _OnCannotResolve_ method will be called. + ### ToString Hint Determines whether to generate the _ToString()_ method. This method provides a class diagram in [mermaid](https://mermaid.js.org/) format. To see this diagram, just call the ToString method and copy the text to [this site](https://mermaid.live/). diff --git a/readme/FooterTemplate.md b/readme/FooterTemplate.md index e6faf3394..bf51f8f16 100644 --- a/readme/FooterTemplate.md +++ b/readme/FooterTemplate.md @@ -282,19 +282,29 @@ DI.Setup("Composition") | [OnNewInstance](#onnewinstance-hint) | _On_ or _Off_ | 9.0 | _Off_ | | [OnNewInstancePartial](#onnewinstance-hint) | _On_ or _Off_ | | _On_ | | [OnNewInstanceImplementationTypeNameRegularExpression](#onnewinstanceimplementationtypenameregularexpression-hint) | Regular expression | | .+ | +| [OnNewInstanceImplementationTypeNameWildcard](#onnewinstanceimplementationtypenamewildcard-hint) | Wildcard | | * | | [OnNewInstanceTagRegularExpression](#onnewinstancetagregularexpression-hint) | Regular expression | | .+ | +| [OnNewInstanceTagWildcard](#onnewinstancetagwildcard-hint) | Wildcard | | * | | [OnNewInstanceLifetimeRegularExpression](#onnewinstancelifetimeregularexpression-hint) | Regular expression | | .+ | +| [OnNewInstanceLifetimeWildcard](#onnewinstancelifetimewildcard-hint) | Wildcard | | * | | [OnDependencyInjection](#ondependencyinjection-hint) | _On_ or _Off_ | 9.0 | _Off_ | | [OnDependencyInjectionPartial](#ondependencyinjectionpartial-hint) | _On_ or _Off_ | | _On_ | | [OnDependencyInjectionImplementationTypeNameRegularExpression](#OnDependencyInjectionImplementationTypeNameRegularExpression-Hint) | Regular expression | | .+ | +| [OnDependencyInjectionImplementationTypeNameWildcard](#OnDependencyInjectionImplementationTypeNameWildcard-Hint) | Wildcard | | * | | [OnDependencyInjectionContractTypeNameRegularExpression](#ondependencyinjectioncontracttypenameregularexpression-hint) | Regular expression | | .+ | +| [OnDependencyInjectionContractTypeNameWildcard](#ondependencyinjectioncontracttypenameWildcard-hint) | Wildcard | | * | | [OnDependencyInjectionTagRegularExpression](#ondependencyinjectiontagregularexpression-hint) | Regular expression | | .+ | +| [OnDependencyInjectionTagWildcard](#ondependencyinjectiontagWildcard-hint) | Wildcard | | * | | [OnDependencyInjectionLifetimeRegularExpression](#ondependencyinjectionlifetimeregularexpression-hint) | Regular expression | | .+ | +| [OnDependencyInjectionLifetimeWildcard](#ondependencyinjectionlifetimeWildcard-hint) | Wildcard | | * | | [OnCannotResolve](#oncannotresolve-hint) | _On_ or _Off_ | 9.0 | _Off_ | | [OnCannotResolvePartial](#oncannotresolvepartial-hint) | _On_ or _Off_ | | _On_ | | [OnCannotResolveContractTypeNameRegularExpression](#oncannotresolvecontracttypenameregularexpression-hint) | Regular expression | | .+ | +| [OnCannotResolveContractTypeNameWildcard](#oncannotresolvecontracttypenameцildcard-hint) | Wildcard | | * | | [OnCannotResolveTagRegularExpression](#oncannotresolvetagregularexpression-hint) | Regular expression | | .+ | +| [OnCannotResolveTagWildcard](#oncannotresolvetagWildcard-hint) | Wildcard | | * | | [OnCannotResolveLifetimeRegularExpression](#oncannotresolvelifetimeregularexpression-hint) | Regular expression | | .+ | +| [OnCannotResolveLifetimeWildcard](#oncannotresolvelifetimeWildcard-hint) | Wildcard | | * | | [OnNewRoot](#onnewroot-hint) | _On_ or _Off_ | | _Off_ | | [OnNewRootPartial](#onnewrootpartial-hint) | _On_ or _Off_ | | _On_ | | [ToString](#tostring-hint) | _On_ or _Off_ | | _Off_ | @@ -342,14 +352,26 @@ Determines whether to generate the _OnNewInstance_ partial method. By default, t This is a regular expression for filtering by instance type name. This hint is useful when _OnNewInstance_ is in _On_ state and it is necessary to limit the set of types for which the _OnNewInstance_ method will be called. +### OnNewInstanceImplementationTypeNameWildcard Hint + +This is a Wildcard for filtering by instance type name. This hint is useful when _OnNewInstance_ is in _On_ state and it is necessary to limit the set of types for which the _OnNewInstance_ method will be called. + ### OnNewInstanceTagRegularExpression Hint This is a regular expression for filtering by _tag_. This hint is also useful when _OnNewInstance_ is in _On_ state and it is necessary to limit the set of _tags_ for which the _OnNewInstance_ method will be called. +### OnNewInstanceTagWildcard Hint + +This is a wildcard for filtering by _tag_. This hint is also useful when _OnNewInstance_ is in _On_ state and it is necessary to limit the set of _tags_ for which the _OnNewInstance_ method will be called. + ### OnNewInstanceLifetimeRegularExpression Hint This is a regular expression for filtering by _lifetime_. This hint is also useful when _OnNewInstance_ is in _On_ state and it is necessary to restrict the set of _life_ times for which the _OnNewInstance_ method will be called. +### OnNewInstanceLifetimeWildcard Hint + +This is a wildcard for filtering by _lifetime_. This hint is also useful when _OnNewInstance_ is in _On_ state and it is necessary to restrict the set of _life_ times for which the _OnNewInstance_ method will be called. + ### OnDependencyInjection Hint Determines whether to use the _OnDependencyInjection_ partial method when the _OnDependencyInjection_ hint is ```On``` to control dependency injection. By default it is ```On```. @@ -381,18 +403,34 @@ To minimize performance loss when calling _OnDependencyInjection_, use the three This is a regular expression for filtering by instance type name. This hint is useful when _OnDependencyInjection_ is in _On_ state and it is necessary to restrict the set of types for which the _OnDependencyInjection_ method will be called. +### OnDependencyInjectionImplementationTypeNameWildcard Hint + +This is a wildcard for filtering by instance type name. This hint is useful when _OnDependencyInjection_ is in _On_ state and it is necessary to restrict the set of types for which the _OnDependencyInjection_ method will be called. + ### OnDependencyInjectionContractTypeNameRegularExpression Hint This is a regular expression for filtering by the name of the resolving type. This hint is also useful when _OnDependencyInjection_ is in _On_ state and it is necessary to limit the set of permissive types for which the _OnDependencyInjection_ method will be called. +### OnDependencyInjectionContractTypeNameWildcard Hint + +This is a wildcard for filtering by the name of the resolving type. This hint is also useful when _OnDependencyInjection_ is in _On_ state and it is necessary to limit the set of permissive types for which the _OnDependencyInjection_ method will be called. + ### OnDependencyInjectionTagRegularExpression Hint This is a regular expression for filtering by _tag_. This hint is also useful when _OnDependencyInjection_ is in the _On_ state and you want to limit the set of _tags_ for which the _OnDependencyInjection_ method will be called. +### OnDependencyInjectionTagWildcard Hint + +This is a wildcard for filtering by _tag_. This hint is also useful when _OnDependencyInjection_ is in the _On_ state and you want to limit the set of _tags_ for which the _OnDependencyInjection_ method will be called. + ### OnDependencyInjectionLifetimeRegularExpression Hint This is a regular expression for filtering by _lifetime_. This hint is also useful when _OnDependencyInjection_ is in _On_ state and it is necessary to restrict the set of _lifetime_ for which the _OnDependencyInjection_ method will be called. +### OnDependencyInjectionLifetimeWildcard Hint + +This is a wildcard for filtering by _lifetime_. This hint is also useful when _OnDependencyInjection_ is in _On_ state and it is necessary to restrict the set of _lifetime_ for which the _OnDependencyInjection_ method will be called. + ### OnCannotResolve Hint Determines whether to use the `OnCannotResolve(...)` partial method to handle a scenario in which an instance cannot be resolved. By default, this partial method is not generated. Because of the return value, it cannot have an empty body and must be overridden at creation. @@ -448,14 +486,26 @@ DI.Setup("Composition") This is a regular expression for filtering by the name of the resolving type. This hint is also useful when _OnCannotResolve_ is in _On_ state and it is necessary to limit the set of resolving types for which the _OnCannotResolve_ method will be called. +### OnCannotResolveContractTypeNameWildcard Hint + +This is a wildcard for filtering by the name of the resolving type. This hint is also useful when _OnCannotResolve_ is in _On_ state and it is necessary to limit the set of resolving types for which the _OnCannotResolve_ method will be called. + ### OnCannotResolveTagRegularExpression Hint This is a regular expression for filtering by _tag_. This hint is also useful when _OnCannotResolve_ is in _On_ state and it is necessary to limit the set of _tags_ for which the _OnCannotResolve_ method will be called. +### OnCannotResolveTagWildcard Hint + +This is a wildcard for filtering by _tag_. This hint is also useful when _OnCannotResolve_ is in _On_ state and it is necessary to limit the set of _tags_ for which the _OnCannotResolve_ method will be called. + ### OnCannotResolveLifetimeRegularExpression Hint This is a regular expression for filtering by _lifetime_. This hint is also useful when _OnCannotResolve_ is in the _On_ state and it is necessary to restrict the set of _lives_ for which the _OnCannotResolve_ method will be called. +### OnCannotResolveLifetimeWildcard Hint + +This is a wildcard for filtering by _lifetime_. This hint is also useful when _OnCannotResolve_ is in the _On_ state and it is necessary to restrict the set of _lives_ for which the _OnCannotResolve_ method will be called. + ### ToString Hint Determines whether to generate the _ToString()_ method. This method provides a class diagram in [mermaid](https://mermaid.js.org/) format. To see this diagram, just call the ToString method and copy the text to [this site](https://mermaid.live/). diff --git a/readme/oncannotresolve-hint.md b/readme/oncannotresolve-regular-expression-hint.md similarity index 94% rename from readme/oncannotresolve-hint.md rename to readme/oncannotresolve-regular-expression-hint.md index 6bf452fcb..d78d7ac18 100644 --- a/readme/oncannotresolve-hint.md +++ b/readme/oncannotresolve-regular-expression-hint.md @@ -1,6 +1,6 @@ -#### OnCannotResolve hint +#### OnCannotResolve regular expression hint -[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Hints/OnCannotResolveHintScenario.cs) +[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Hints/OnCannotResolveRegularExpressionHintScenario.cs) Hints are used to fine-tune code generation. The _OnCannotResolve_ hint determines whether to generate a partial `OnCannotResolve(...)` method to handle a scenario where an instance which cannot be resolved. In addition, setup hints can be comments before the _Setup_ method in the form ```hint = value```, for example: `// OnCannotResolveContractTypeNameRegularExpression = string`. @@ -133,7 +133,7 @@ classDiagram Composition ..> Service : IService Root Service *-- Dependency : IDependency Dependency *-- String : String - namespace Pure.DI.UsageTests.Hints.OnCannotResolveHintScenario { + namespace Pure.DI.UsageTests.Hints.OnCannotResolveRegularExpressionHintScenario { class Composition { <> +IService Root diff --git a/readme/oncannotresolve-wildcard-hint.md b/readme/oncannotresolve-wildcard-hint.md new file mode 100644 index 000000000..f64dbfac3 --- /dev/null +++ b/readme/oncannotresolve-wildcard-hint.md @@ -0,0 +1,159 @@ +#### OnCannotResolve wildcard hint + +[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Hints/OnCannotResolveWildcardHintScenario.cs) + +Hints are used to fine-tune code generation. The _OnCannotResolve_ hint determines whether to generate a partial `OnCannotResolve(...)` method to handle a scenario where an instance which cannot be resolved. +In addition, setup hints can be comments before the _Setup_ method in the form ```hint = value```, for example: `// OnCannotResolveContractTypeNameWildcard = string`. + + +```c# +using Shouldly; +using Pure.DI; +using static Pure.DI.Hint; + +// OnCannotResolveContractTypeNameWildcard = string +DI.Setup(nameof(Composition)) + .Hint(OnCannotResolve, "On") + .Bind().To() + .Bind().To() + .Root("Root"); + +var composition = new Composition(); +var service = composition.Root; +service.Dependency.ToString().ShouldBe("My name"); + + +interface IDependency; + +class Dependency(string name) : IDependency +{ + public override string ToString() => name; +} + +interface IService +{ + IDependency Dependency { get; } +} + +class Service(IDependency dependency) : IService +{ + public IDependency Dependency { get; } = dependency; +} + +partial class Composition +{ + private partial T OnCannotResolve( + object? tag, + Lifetime lifetime) + { + if (typeof(T) == typeof(string)) + { + return (T)(object)"My name"; + } + + throw new InvalidOperationException("Cannot resolve."); + } +} +``` + +
+Running this code sample locally + +- Make sure you have the [.NET SDK 9.0](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) or later is installed +```bash +dotnet --list-sdk +``` +- Create a net9.0 (or later) console application +```bash +dotnet new console -n Sample +``` +- Add references to NuGet packages + - [Pure.DI](https://www.nuget.org/packages/Pure.DI) + - [Shouldly](https://www.nuget.org/packages/Shouldly) +```bash +dotnet add package Pure.DI +dotnet add package Shouldly +``` +- Copy the example code into the _Program.cs_ file + +You are ready to run the example 🚀 +```bash +dotnet run +``` + +
+ +The `OnCannotResolveContractTypeNameWildcard` hint helps define the set of types that require manual dependency resolution. You can use it to specify a wildcard to filter the full type name. +For more hints, see [this](README.md#setup-hints) page. + +The following partial class will be generated: + +```c# +partial class Composition +{ + private readonly Composition _root; + + [OrdinalAttribute(256)] + public Composition() + { + _root = this; + } + + internal Composition(Composition parentScope) + { + _root = (parentScope ?? throw new ArgumentNullException(nameof(parentScope)))._root; + } + + public IService Root + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + string transientString2 = OnCannotResolve(null, Lifetime.Transient); + return new Service(new Dependency(transientString2)); + } + } + + + private partial T OnCannotResolve(object? tag, Lifetime lifetime); +} +``` + +Class diagram: + +```mermaid +--- + config: + class: + hideEmptyMembersBox: true +--- +classDiagram + Service --|> IService + Dependency --|> IDependency + Composition ..> Service : IService Root + Service *-- Dependency : IDependency + Dependency *-- String : String + namespace Pure.DI.UsageTests.Hints.OnCannotResolveWildcardHintScenario { + class Composition { + <> + +IService Root + } + class Dependency { + +Dependency(String name) + } + class IDependency { + <> + } + class IService { + <> + } + class Service { + +Service(IDependency dependency) + } + } + namespace System { + class String { + } + } +``` + diff --git a/readme/ondependencyinjection-hint.md b/readme/ondependencyinjection-regular-expression-hint.md similarity index 94% rename from readme/ondependencyinjection-hint.md rename to readme/ondependencyinjection-regular-expression-hint.md index a1359d5bf..89e3801f4 100644 --- a/readme/ondependencyinjection-hint.md +++ b/readme/ondependencyinjection-regular-expression-hint.md @@ -1,6 +1,6 @@ -#### OnDependencyInjection hint +#### OnDependencyInjection regular expression hint -[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionHintScenario.cs) +[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionRegularExpressionHintScenario.cs) Hints are used to fine-tune code generation. The _OnDependencyInjection_ hint determines whether to generate partial _OnDependencyInjection_ method to control of dependency injection. In addition, setup hints can be comments before the _Setup_ method in the form ```hint = value```, for example: `// OnDependencyInjection = On`. @@ -134,7 +134,7 @@ classDiagram Composition ..> Service : IService GetRoot(int id) Service *-- Dependency : IDependency Dependency o-- Int32 : Argument "id" - namespace Pure.DI.UsageTests.Hints.OnDependencyInjectionHintScenario { + namespace Pure.DI.UsageTests.Hints.OnDependencyInjectionRegularExpressionHintScenario { class Composition { <> +IService GetRoot(int id) diff --git a/readme/ondependencyinjection-wildcard-hint.md b/readme/ondependencyinjection-wildcard-hint.md new file mode 100644 index 000000000..af1efd729 --- /dev/null +++ b/readme/ondependencyinjection-wildcard-hint.md @@ -0,0 +1,162 @@ +#### OnDependencyInjection wildcard hint + +[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionWildcardHintScenario.cs) + +Hints are used to fine-tune code generation. The _OnDependencyInjection_ hint determines whether to generate partial _OnDependencyInjection_ method to control of dependency injection. +In addition, setup hints can be comments before the _Setup_ method in the form ```hint = value```, for example: `// OnDependencyInjection = On`. + + +```c# +using Shouldly; +using Pure.DI; +using static Pure.DI.Hint; + +// OnDependencyInjection = On +DI.Setup(nameof(Composition)) + .Hint(OnDependencyInjectionContractTypeNameWildcard, "*IDependency") + .RootArg("id") + .Bind().To() + .Bind().To() + .Root("GetRoot"); + +var log = new List(); +var composition = new Composition(log); +var service = composition.GetRoot(33); + +log.ShouldBe(["Dependency injected"]); + +interface IDependency; + +record Dependency(int Id) : IDependency; + +interface IService +{ + IDependency Dependency { get; } +} + +class Service(IDependency dependency) : IService +{ + public IDependency Dependency { get; } = dependency; +} + +partial class Composition +{ + private readonly List _log = []; + + public Composition(List log) : this() => + _log = log; + + private partial T OnDependencyInjection( + in T value, + object? tag, + Lifetime lifetime) + { + _log.Add($"{value?.GetType().Name} injected"); + return value; + } +} +``` + +
+Running this code sample locally + +- Make sure you have the [.NET SDK 9.0](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) or later is installed +```bash +dotnet --list-sdk +``` +- Create a net9.0 (or later) console application +```bash +dotnet new console -n Sample +``` +- Add references to NuGet packages + - [Pure.DI](https://www.nuget.org/packages/Pure.DI) + - [Shouldly](https://www.nuget.org/packages/Shouldly) +```bash +dotnet add package Pure.DI +dotnet add package Shouldly +``` +- Copy the example code into the _Program.cs_ file + +You are ready to run the example 🚀 +```bash +dotnet run +``` + +
+ +The `OnDependencyInjectionContractTypeNameWildcard` hint helps identify the set of types that require injection control. You can use it to specify a wildcard to filter the full name of a type. +For more hints, see [this](README.md#setup-hints) page. + +The following partial class will be generated: + +```c# +partial class Composition +{ + private readonly Composition _root; + + [OrdinalAttribute(128)] + public Composition() + { + _root = this; + } + + internal Composition(Composition parentScope) + { + _root = (parentScope ?? throw new ArgumentNullException(nameof(parentScope)))._root; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IService GetRoot(int id) + { + return new Service(OnDependencyInjection(new Dependency(id), null, Lifetime.Transient)); + } + + + private partial T OnDependencyInjection(in T value, object? tag, Lifetime lifetime); +} +``` + +Class diagram: + +```mermaid +--- + config: + class: + hideEmptyMembersBox: true +--- +classDiagram + Service --|> IService + Dependency --|> IDependency + Dependency --|> IEquatableᐸDependencyᐳ + Composition ..> Service : IService GetRoot(int id) + Service *-- Dependency : IDependency + Dependency o-- Int32 : Argument "id" + namespace Pure.DI.UsageTests.Hints.OnDependencyInjectionWildcardHintScenario { + class Composition { + <> + +IService GetRoot(int id) + } + class Dependency { + <> + +Dependency(Int32 Id) + } + class IDependency { + <> + } + class IService { + <> + } + class Service { + +Service(IDependency dependency) + } + } + namespace System { + class IEquatableᐸDependencyᐳ { + <> + } + class Int32 { + <> + } + } +``` + diff --git a/readme/onnewinstance-hint.md b/readme/onnewinstance-regular-expression-hint.md similarity index 88% rename from readme/onnewinstance-hint.md rename to readme/onnewinstance-regular-expression-hint.md index 4747b9000..76caac788 100644 --- a/readme/onnewinstance-hint.md +++ b/readme/onnewinstance-regular-expression-hint.md @@ -1,6 +1,6 @@ -#### OnNewInstance hint +#### OnNewInstance regular expression hint -[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Hints/OnNewInstanceHintScenario.cs) +[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Hints/OnNewInstanceRegularExpressionHintScenario.cs) Hints are used to fine-tune code generation. The _OnNewInstance_ hint determines whether to generate partial _OnNewInstance_ method. In addition, setup hints can be comments before the _Setup_ method in the form ```hint = value```, for example: `// OnNewInstance = On`. @@ -13,8 +13,9 @@ using static Pure.DI.Hint; DI.Setup(nameof(Composition)) .Hint(OnNewInstance, "On") + .Hint(OnNewInstanceLifetimeRegularExpression, "(Singleton|PerBlock)") .Bind().As(Lifetime.Singleton).To() - .Bind().To() + .Bind().As(Lifetime.PerBlock).To() .Root("Root"); var log = new List(); @@ -134,9 +135,9 @@ partial class Composition } } - Service transientService0 = new Service(_root._singletonDependency43); - OnNewInstance(ref transientService0, null, Lifetime.Transient); - return transientService0; + Service perBlockService0 = new Service(_root._singletonDependency43); + OnNewInstance(ref perBlockService0, null, Lifetime.PerBlock); + return perBlockService0; } } @@ -158,7 +159,7 @@ classDiagram Dependency --|> IDependency Composition ..> Service : IService Root Service o-- "Singleton" Dependency : IDependency - namespace Pure.DI.UsageTests.Hints.OnNewInstanceHintScenario { + namespace Pure.DI.UsageTests.Hints.OnNewInstanceRegularExpressionHintScenario { class Composition { <> +IService Root diff --git a/readme/onnewinstance-wildcard-hint.md b/readme/onnewinstance-wildcard-hint.md new file mode 100644 index 000000000..7263e3201 --- /dev/null +++ b/readme/onnewinstance-wildcard-hint.md @@ -0,0 +1,176 @@ +#### OnNewInstance wildcard hint + +[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Hints/OnNewInstanceWildcardHintScenario.cs) + +Hints are used to fine-tune code generation. The _OnNewInstance_ hint determines whether to generate partial _OnNewInstance_ method. +In addition, setup hints can be comments before the _Setup_ method in the form ```hint = value```, for example: `// OnNewInstance = On`. + + +```c# +using Shouldly; +using Pure.DI; +using static Pure.DI.Hint; + +DI.Setup(nameof(Composition)) + .Hint(OnNewInstance, "On") + .Hint(OnNewInstanceImplementationTypeNameWildcard, "*Service") + .Bind().As(Lifetime.Singleton).To() + .Bind().To() + .Root("Root"); + +var log = new List(); +var composition = new Composition(log); +var service1 = composition.Root; +var service2 = composition.Root; + +log.ShouldBe([ + "Service created", + "Service created"]); + +interface IDependency; + +class Dependency : IDependency +{ + public override string ToString() => nameof(Dependency); +} + +interface IService +{ + IDependency Dependency { get; } +} + +class Service(IDependency dependency) : IService +{ + public IDependency Dependency { get; } = dependency; + + public override string ToString() => nameof(Service); +} + +internal partial class Composition +{ + private readonly List _log = []; + + public Composition(List log) : this() => + _log = log; + + partial void OnNewInstance( + ref T value, + object? tag, + Lifetime lifetime) => + _log.Add($"{typeof(T).Name} created"); +} +``` + +
+Running this code sample locally + +- Make sure you have the [.NET SDK 9.0](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) or later is installed +```bash +dotnet --list-sdk +``` +- Create a net9.0 (or later) console application +```bash +dotnet new console -n Sample +``` +- Add references to NuGet packages + - [Pure.DI](https://www.nuget.org/packages/Pure.DI) + - [Shouldly](https://www.nuget.org/packages/Shouldly) +```bash +dotnet add package Pure.DI +dotnet add package Shouldly +``` +- Copy the example code into the _Program.cs_ file + +You are ready to run the example 🚀 +```bash +dotnet run +``` + +
+ +The `OnNewInstanceImplementationTypeNameWildcard` hint helps you define a set of implementation types that require instance creation control. You can use it to specify a wildcard to filter bindings by implementation name. +For more hints, see [this](README.md#setup-hints) page. + +The following partial class will be generated: + +```c# +partial class Composition +{ + private readonly Composition _root; + private readonly Lock _lock; + + private Dependency? _singletonDependency43; + + [OrdinalAttribute(256)] + public Composition() + { + _root = this; + _lock = new Lock(); + } + + internal Composition(Composition parentScope) + { + _root = (parentScope ?? throw new ArgumentNullException(nameof(parentScope)))._root; + _lock = _root._lock; + } + + public IService Root + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (_root._singletonDependency43 is null) + { + using (_lock.EnterScope()) + { + if (_root._singletonDependency43 is null) + { + _root._singletonDependency43 = new Dependency(); + } + } + } + + Service transientService0 = new Service(_root._singletonDependency43); + OnNewInstance(ref transientService0, null, Lifetime.Transient); + return transientService0; + } + } + + + partial void OnNewInstance(ref T value, object? tag, Lifetime lifetime); +} +``` + +Class diagram: + +```mermaid +--- + config: + class: + hideEmptyMembersBox: true +--- +classDiagram + Service --|> IService + Dependency --|> IDependency + Composition ..> Service : IService Root + Service o-- "Singleton" Dependency : IDependency + namespace Pure.DI.UsageTests.Hints.OnNewInstanceWildcardHintScenario { + class Composition { + <> + +IService Root + } + class Dependency { + +Dependency() + } + class IDependency { + <> + } + class IService { + <> + } + class Service { + +Service(IDependency dependency) + } + } +``` + diff --git a/src/Pure.DI.Core/Components/Api.g.cs b/src/Pure.DI.Core/Components/Api.g.cs index f03e6c366..0f01ac036 100644 --- a/src/Pure.DI.Core/Components/Api.g.cs +++ b/src/Pure.DI.Core/Components/Api.g.cs @@ -199,6 +199,26 @@ internal enum Hint /// OnNewInstanceImplementationTypeNameRegularExpression, + /// + /// The wildcard to filter OnNewInstance by the instance type name. "*" by default. + /// + /// + /// // OnNewInstanceImplementationTypeNameWildcard = *Dependency + /// DI.Setup("Composition") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ /// or using the API call : + /// + /// DI.Setup("Composition") + /// .Hint(Hint.OnNewInstanceImplementationTypeNameWildcard, "*Dependency") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ ///
+ /// + OnNewInstanceImplementationTypeNameWildcard, + /// /// The regular expression to filter OnNewInstance by the tag. ".+" by default. /// @@ -219,6 +239,26 @@ internal enum Hint /// OnNewInstanceTagRegularExpression, + /// + /// The wildcard to filter OnNewInstance by the tag. "*" by default. + /// + /// + /// // OnNewInstanceTagWildcard = *IDependency + /// DI.Setup("Composition") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ /// or using the API call : + /// + /// DI.Setup("Composition") + /// .Hint(Hint.OnNewInstanceTagWildcard, "*IDependency") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ ///
+ /// + OnNewInstanceTagWildcard, + /// /// The regular expression to filter OnNewInstance by the lifetime. ".+" by default. /// @@ -239,6 +279,26 @@ internal enum Hint /// OnNewInstanceLifetimeRegularExpression, + /// + /// The wildcard to filter OnNewInstance by the lifetime. "*" by default. + /// + /// + /// // OnNewInstanceLifetimeWildcard = *Singleton + /// DI.Setup("Composition") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ /// or using the API call : + /// + /// DI.Setup("Composition") + /// .Hint(Hint.OnNewInstanceLifetimeWildcard, "*Singleton") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ ///
+ /// + OnNewInstanceLifetimeWildcard, + /// /// On or Off. Determines whether to use partial OnDependencyInjection method to control of dependency injection. Off by default. /// @@ -299,6 +359,26 @@ internal enum Hint /// OnDependencyInjectionImplementationTypeNameRegularExpression, + /// + /// The wildcard to filter OnDependencyInjection by the instance type name. "*" by default. + /// + /// + /// // OnDependencyInjectionImplementationTypeNameWildcard = *Dependency + /// DI.Setup("Composition") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ /// or using the API call : + /// + /// DI.Setup("Composition") + /// .Hint(Hint.OnDependencyInjectionImplementationTypeNameWildcard, "*Dependency") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ ///
+ /// + OnDependencyInjectionImplementationTypeNameWildcard, + /// /// The regular expression to filter OnDependencyInjection by the resolving type name. ".+" by default. /// @@ -319,6 +399,26 @@ internal enum Hint /// OnDependencyInjectionContractTypeNameRegularExpression, + /// + /// The wildcard to filter OnDependencyInjection by the resolving type name. "*" by default. + /// + /// + /// // OnDependencyInjectionContractTypeNameWildcard = *IDependency + /// DI.Setup("Composition") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ /// or using the API call : + /// + /// DI.Setup("Composition") + /// .Hint(Hint.OnDependencyInjectionContractTypeName, "*IDependency") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ ///
+ /// + OnDependencyInjectionContractTypeNameWildcard, + /// /// The regular expression to filter OnDependencyInjection by the tag. ".+" by default. /// @@ -339,6 +439,26 @@ internal enum Hint /// OnDependencyInjectionTagRegularExpression, + /// + /// The wildcard to filter OnDependencyInjection by the tag. "*" by default. + /// + /// + /// // OnDependencyInjectionTagWildcard = MyTag + /// DI.Setup("Composition") + /// .Bind<IDependency>("MyTag").To<Dependency>(); + /// + ///
+ /// or using the API call : + /// + /// DI.Setup("Composition") + /// .Hint(Hint.OnDependencyInjectionTagWildcard, "MyTag") + /// .Bind<IDependency>("MyTag").To<Dependency>(); + /// + ///
+ ///
+ /// + OnDependencyInjectionTagWildcard, + /// /// The regular expression to filter OnDependencyInjection by the lifetime. ".+" by default. /// @@ -359,6 +479,26 @@ internal enum Hint /// OnDependencyInjectionLifetimeRegularExpression, + /// + /// The wildcard to filter OnDependencyInjection by the lifetime. ".+" by default. + /// + /// + /// // OnDependencyInjectionLifetimeWildcard = *Singleton + /// DI.Setup("Composition") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ /// or using the API call : + /// + /// DI.Setup("Composition") + /// .Hint(Hint.OnDependencyInjectionLifetimeWildcard, "*Singleton") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ ///
+ /// + OnDependencyInjectionLifetimeWildcard, + /// /// On or Off. Determines whether to use a partial OnCannotResolve<T>(...) method to handle a scenario in which the dependency cannot be resolved. Off by default. /// @@ -419,6 +559,26 @@ internal enum Hint /// OnCannotResolveContractTypeNameRegularExpression, + /// + /// The wildcard to filter OnCannotResolve by the resolving type name. "*" by default. + /// + /// + /// // OnCannotResolveContractTypeNameWildcard = *OtherType + /// DI.Setup("Composition") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ /// or using the API call : + /// + /// DI.Setup("Composition") + /// .Hint(Hint.OnCannotResolveContractTypeNameWildcard, "*OtherType") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ ///
+ /// + OnCannotResolveContractTypeNameWildcard, + /// /// The regular expression to filter OnCannotResolve by the tag. ".+" by default. /// @@ -439,6 +599,26 @@ internal enum Hint /// OnCannotResolveTagRegularExpression, + /// + /// The wildcard to filter OnCannotResolve by the tag. "*" by default. + /// + /// + /// // OnCannotResolveTagWildcard = MyTag + /// DI.Setup("Composition") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ /// or using the API call : + /// + /// DI.Setup("Composition") + /// .Hint(Hint.OnCannotResolveTagWildcard, "MyTag") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ ///
+ /// + OnCannotResolveTagWildcard, + /// /// The regular expression to filter OnCannotResolve by the lifetime. ".+" by default. /// @@ -459,6 +639,26 @@ internal enum Hint /// OnCannotResolveLifetimeRegularExpression, + /// + /// The wildcard to filter OnCannotResolve by the lifetime. "*" by default. + /// + /// + /// // OnCannotResolveLifetimeWildcard = *Singleton + /// DI.Setup("Composition") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ /// or using the API call : + /// + /// DI.Setup("Composition") + /// .Hint(Hint.OnCannotResolveLifetimeWildcard, "*Singleton") + /// .Bind<IDependency>().To<Dependency>(); + /// + ///
+ ///
+ /// + OnCannotResolveLifetimeWildcard, + /// /// On or Off. Determines whether to use a static partial OnNewRoot<T>(...) method to handle the new Composition root registration event. Off by default. /// diff --git a/src/Pure.DI.Core/Core/ApiInvocationProcessor.cs b/src/Pure.DI.Core/Core/ApiInvocationProcessor.cs index 7468d2a09..ac37eee49 100644 --- a/src/Pure.DI.Core/Core/ApiInvocationProcessor.cs +++ b/src/Pure.DI.Core/Core/ApiInvocationProcessor.cs @@ -67,7 +67,7 @@ MemberAccessExpressionSyntax memberAccess when memberAccess.Kind() == SyntaxKind if (type is INamedTypeSymbol symbol) { if (symbol.TypeArguments.Length > 1 - && symbolNames.GetDisplayName(symbol.TypeArguments[0]) != Names.ContextInterfaceName) + && symbolNames.GetGlobalName(symbol.TypeArguments[0]) != Names.IContextTypeName) { switch (invocation.ArgumentList.Arguments[0].Expression) { diff --git a/src/Pure.DI.Core/Core/Attributes.cs b/src/Pure.DI.Core/Core/Attributes.cs index 9c912b458..60f6c831b 100644 --- a/src/Pure.DI.Core/Core/Attributes.cs +++ b/src/Pure.DI.Core/Core/Attributes.cs @@ -115,7 +115,7 @@ private IReadOnlyList GetAttributes(ISymbol member, INamedTypeSym return false; } - return symbolNames.GetDisplayName(unboundTypeSymbol) == symbolNames.GetDisplayName(attributeType); + return symbolNames.GetGlobalName(unboundTypeSymbol) == symbolNames.GetGlobalName(attributeType); }) .ToArray(); diff --git a/src/Pure.DI.Core/Core/Code/BuildTools.cs b/src/Pure.DI.Core/Core/Code/BuildTools.cs index a27354c46..07ef08489 100644 --- a/src/Pure.DI.Core/Core/Code/BuildTools.cs +++ b/src/Pure.DI.Core/Core/Code/BuildTools.cs @@ -70,13 +70,22 @@ private string OnInjectedInternal(BuildContext ctx, Variable variable) } var tag = GetTag(ctx, variable); - // ReSharper disable once ConvertIfStatementToReturnStatement - if (!filter.IsMeetRegularExpression( + var typeName = new Lazy(() => typeResolver.Resolve(variable.Setup, variable.InstanceType).Name); + var contractName = new Lazy(() => symbolNames.GetName(variable.ContractType)); + var tagName = new Lazy(() => tag.ValueToString()); + var lifetimeName = new Lazy(() => variable.Node.Lifetime.ValueToString()); + if (!filter.IsMeetRegularExpressions( ctx.DependencyGraph.Source, - (Hint.OnDependencyInjectionImplementationTypeNameRegularExpression, typeResolver.Resolve(variable.Setup, variable.InstanceType).Name), - (Hint.OnDependencyInjectionContractTypeNameRegularExpression, symbolNames.GetDisplayName(variable.ContractType)), - (Hint.OnDependencyInjectionTagRegularExpression, tag.ValueToString()), - (Hint.OnDependencyInjectionLifetimeRegularExpression, variable.Node.Lifetime.ValueToString()))) + (Hint.OnDependencyInjectionImplementationTypeNameRegularExpression, typeName), + (Hint.OnDependencyInjectionContractTypeNameRegularExpression, contractName), + (Hint.OnDependencyInjectionTagRegularExpression, tagName), + (Hint.OnDependencyInjectionLifetimeRegularExpression, lifetimeName)) + || !filter.IsMeetWildcards( + ctx.DependencyGraph.Source, + (Hint.OnDependencyInjectionImplementationTypeNameWildcard, typeName), + (Hint.OnDependencyInjectionContractTypeNameWildcard, contractName), + (Hint.OnDependencyInjectionTagWildcard, tagName), + (Hint.OnDependencyInjectionLifetimeWildcard, lifetimeName))) { return variableCode; } @@ -128,11 +137,19 @@ public IEnumerable OnCreated(BuildContext ctx, Variable variable) } var tag = GetTag(ctx, variable); - if (!filter.IsMeetRegularExpression( + var typeName = new Lazy(() => symbolNames.GetName(variable.Node.Type)); + var tagName = new Lazy(() => tag.ValueToString()); + var lifetimeName = new Lazy(() => variable.Node.Lifetime.ValueToString()); + if (!filter.IsMeetRegularExpressions( + ctx.DependencyGraph.Source, + (Hint.OnNewInstanceImplementationTypeNameRegularExpression, typeName), + (Hint.OnNewInstanceTagRegularExpression, tagName), + (Hint.OnNewInstanceLifetimeRegularExpression, lifetimeName)) + || !filter.IsMeetWildcards( ctx.DependencyGraph.Source, - (Hint.OnNewInstanceImplementationTypeNameRegularExpression, variable.Node.Type.ToString()), - (Hint.OnNewInstanceTagRegularExpression, tag.ValueToString()), - (Hint.OnNewInstanceLifetimeRegularExpression, variable.Node.Lifetime.ValueToString()))) + (Hint.OnNewInstanceImplementationTypeNameWildcard, typeName), + (Hint.OnNewInstanceTagWildcard, tagName), + (Hint.OnNewInstanceLifetimeWildcard, lifetimeName))) { return code.Lines; } diff --git a/src/Pure.DI.Core/Core/Code/CompositionClassBuilder.cs b/src/Pure.DI.Core/Core/Code/CompositionClassBuilder.cs index 1d2a0bf38..0b92912fd 100644 --- a/src/Pure.DI.Core/Core/Code/CompositionClassBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/CompositionClassBuilder.cs @@ -70,12 +70,12 @@ public CompositionCode Build(CompositionCode composition) var implementingInterfaces = new List(); if (composition.TotalDisposablesCount > 0) { - implementingInterfaces.Add(Names.IDisposableInterfaceName); + implementingInterfaces.Add(Names.IDisposableTypeName); } if (composition.AsyncDisposableCount > 0) { - implementingInterfaces.Add(Names.IAsyncDisposableInterfaceName); + implementingInterfaces.Add(Names.IAsyncDisposableTypeName); } code.AppendLine($"partial class {name.ClassName}{(implementingInterfaces.Count > 0 ? ": " + string.Join(", ", implementingInterfaces) : "")}"); diff --git a/src/Pure.DI.Core/Core/Code/DisposeMethodBuilder.cs b/src/Pure.DI.Core/Core/Code/DisposeMethodBuilder.cs index 3b0fcbb36..9b34998ec 100644 --- a/src/Pure.DI.Core/Core/Code/DisposeMethodBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/DisposeMethodBuilder.cs @@ -72,7 +72,7 @@ public CompositionCode Build(CompositionCode composition) code.AppendLine("/// The disposable instance."); code.AppendLine("/// Exception occurring during disposal."); code.AppendLine("/// The actual type of instance being disposed of."); - code.AppendLine($"partial void {Names.OnDisposeExceptionMethodName}(T disposableInstance, Exception exception) where T : {Names.IDisposableInterfaceName};"); + code.AppendLine($"partial void {Names.OnDisposeExceptionMethodName}(T disposableInstance, Exception exception) where T : {Names.IDisposableTypeName};"); membersCounter++; // ReSharper disable once InvertIf @@ -132,7 +132,7 @@ public CompositionCode Build(CompositionCode composition) code.AppendLine("/// The disposable instance."); code.AppendLine("/// Exception occurring during disposal."); code.AppendLine("/// The actual type of instance being disposed of."); - code.AppendLine($"partial void {Names.OnDisposeAsyncExceptionMethodName}(T asyncDisposableInstance, Exception exception) where T : {Names.IAsyncDisposableInterfaceName};"); + code.AppendLine($"partial void {Names.OnDisposeAsyncExceptionMethodName}(T asyncDisposableInstance, Exception exception) where T : {Names.IAsyncDisposableTypeName};"); membersCounter++; } @@ -141,7 +141,7 @@ public CompositionCode Build(CompositionCode composition) private static void AddDisposeAsyncPart(LinesBuilder code, bool makeAsyncCall) { - code.AppendLine($"case {Names.IAsyncDisposableInterfaceName} asyncDisposableInstance:"); + code.AppendLine($"case {Names.IAsyncDisposableTypeName} asyncDisposableInstance:"); using (code.Indent()) { code.AppendLine("try"); @@ -181,7 +181,7 @@ private static void AddDisposeAsyncPart(LinesBuilder code, bool makeAsyncCall) private static void AddDisposePart(LinesBuilder code) { - code.AppendLine($"case {Names.IDisposableInterfaceName} disposableInstance:"); + code.AppendLine($"case {Names.IDisposableTypeName} disposableInstance:"); using (code.Indent()) { code.AppendLine("try"); diff --git a/src/Pure.DI.Core/Core/Code/FactoryCodeBuilder.cs b/src/Pure.DI.Core/Core/Code/FactoryCodeBuilder.cs index 3915dc14c..29f19534b 100644 --- a/src/Pure.DI.Core/Core/Code/FactoryCodeBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/FactoryCodeBuilder.cs @@ -70,7 +70,7 @@ public void Build(BuildContext ctx, in DpFactory factory) ExpressionSyntax? value = null; var type = memberResolver.ContractType; ExpressionSyntax instance = member.IsStatic - ? SyntaxFactory.ParseTypeName(symbolNames.GetDisplayName(type)) + ? SyntaxFactory.ParseTypeName(symbolNames.GetGlobalName(type)) : SyntaxFactory.IdentifierName(DefaultInstanceValueName); switch (member) @@ -102,7 +102,7 @@ public void Build(BuildContext ctx, in DpFactory factory) argType = bindingTypeConstructor.Construct(setup, binding.SemanticModel.Compilation, argType); } - var typeName = symbolNames.GetDisplayName(argType); + var typeName = symbolNames.GetGlobalName(argType); typeArgs.Add(SyntaxFactory.ParseTypeName(typeName)); } diff --git a/src/Pure.DI.Core/Core/Code/FactoryRewriter.cs b/src/Pure.DI.Core/Core/Code/FactoryRewriter.cs index 956dfb579..f5324455a 100644 --- a/src/Pure.DI.Core/Core/Code/FactoryRewriter.cs +++ b/src/Pure.DI.Core/Core/Code/FactoryRewriter.cs @@ -256,7 +256,7 @@ private bool TryInitialize( return Visit(SyntaxFactory.ParseExpression($" {variable.Injection.Tag.ValueToString()}")); case nameof(IContext.ConsumerTypes): - var consumers = variable.Info.GetTargetNodes().Select(targetNode => $"typeof({symbolNames.GetDisplayName(targetNode.Type)})").ToList(); + var consumers = variable.Info.GetTargetNodes().Select(targetNode => $"typeof({symbolNames.GetGlobalName(targetNode.Type)})").ToList(); if (consumers.Count == 0 && _ctx is not null) { consumers.Add($"typeof({_ctx.DependencyGraph.Source.Name.FullName})"); diff --git a/src/Pure.DI.Core/Core/Code/ResolverClassesBuilder.cs b/src/Pure.DI.Core/Core/Code/ResolverClassesBuilder.cs index 99a72146f..cf55c9050 100644 --- a/src/Pure.DI.Core/Core/Code/ResolverClassesBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/ResolverClassesBuilder.cs @@ -23,11 +23,11 @@ public CompositionCode Build(CompositionCode composition) membersCount++; code.AppendLine(); - code.AppendLine($"private class {Names.ResolverClassName}: {Names.ResolverInterfaceName}<{composition.Source.Source.Name.ClassName}, T>"); + code.AppendLine($"private class {Names.ResolverClassName}: {Names.IResolverTypeName}<{composition.Source.Source.Name.ClassName}, T>"); code.AppendLine("{"); using (code.Indent()) { - code.AppendLine($"public static {Names.ResolverInterfaceName}<{composition.Source.Source.Name.ClassName}, T> {Names.ResolverPropertyName} = new {Names.ResolverClassName}();"); + code.AppendLine($"public static {Names.IResolverTypeName}<{composition.Source.Source.Name.ClassName}, T> {Names.ResolverPropertyName} = new {Names.ResolverClassName}();"); code.AppendLine(); code.AppendLine($"public virtual T {Names.ResolveMethodName}({composition.Source.Source.Name.ClassName} composite)"); code.AppendLine("{"); @@ -60,7 +60,7 @@ public CompositionCode Build(CompositionCode composition) var objectTypeName = ""; if (resolver.Type.IsValueType) { - objectTypeName = $"{Names.ResolverInterfaceName}<{composition.Source.Source.Name.ClassName}, object>"; + objectTypeName = $"{Names.IResolverTypeName}<{composition.Source.Source.Name.ClassName}, object>"; baseTypes.Add(objectTypeName); } diff --git a/src/Pure.DI.Core/Core/Code/ResolverFieldsBuilder.cs b/src/Pure.DI.Core/Core/Code/ResolverFieldsBuilder.cs index 3f100b6b9..aa73fd249 100644 --- a/src/Pure.DI.Core/Core/Code/ResolverFieldsBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/ResolverFieldsBuilder.cs @@ -20,7 +20,7 @@ public CompositionCode Build(CompositionCode composition) var code = composition.Code; code.AppendLine($"private readonly static int {Names.BucketSizeFieldName};"); - var pairs = $"{Names.SystemNamespace}Type, {Names.ResolverInterfaceName}<{composition.Source.Source.Name.ClassName}, object>"; + var pairs = $"{Names.SystemNamespace}Type, {Names.IResolverTypeName}<{composition.Source.Source.Name.ClassName}, object>"; var pairTypeName = $"{Names.ApiNamespace}Pair<{pairs}>"; code.AppendLine($"private readonly static {pairTypeName}[] {Names.BucketsFieldName};"); diff --git a/src/Pure.DI.Core/Core/Code/StaticConstructorBuilder.cs b/src/Pure.DI.Core/Core/Code/StaticConstructorBuilder.cs index a679ffb5a..ba692572d 100644 --- a/src/Pure.DI.Core/Core/Code/StaticConstructorBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/StaticConstructorBuilder.cs @@ -20,7 +20,7 @@ public CompositionCode Build(CompositionCode composition) // ReSharper disable once InvertIf if (hasOnNewRoot && composition.Source.Source.Hints.IsOnNewRootPartial) { - code.AppendLine($"private static partial void {Names.OnNewRootMethodName}({Names.ResolverInterfaceName}<{composition.Source.Source.Name.ClassName}, TContract> resolver, string name, object? tag, {Names.ApiNamespace}{nameof(Lifetime)} lifetime);"); + code.AppendLine($"private static partial void {Names.OnNewRootMethodName}({Names.IResolverTypeName}<{composition.Source.Source.Name.ClassName}, TContract> resolver, string name, object? tag, {Names.ApiNamespace}{nameof(Lifetime)} lifetime);"); code.AppendLine(); membersCounter++; } @@ -51,7 +51,7 @@ public CompositionCode Build(CompositionCode composition) } var divisor = Buckets.GetDivisor((uint)resolvers.Length); - var pairs = $"{Names.SystemNamespace}Type, {Names.ResolverInterfaceName}<{composition.Source.Source.Name.ClassName}, object>"; + var pairs = $"{Names.SystemNamespace}Type, {Names.IResolverTypeName}<{composition.Source.Source.Name.ClassName}, object>"; var bucketsTypeName = $"{Names.ApiNamespace}Buckets<{pairs}>"; var pairTypeName = $"{Names.ApiNamespace}Pair<{pairs}>"; code.AppendLine($"{Names.BucketsFieldName} = {bucketsTypeName}.{nameof(Buckets.Create)}("); diff --git a/src/Pure.DI.Core/Core/Code/TypeResolver.cs b/src/Pure.DI.Core/Core/Code/TypeResolver.cs index fbb42c4b0..668ed4b10 100644 --- a/src/Pure.DI.Core/Core/Code/TypeResolver.cs +++ b/src/Pure.DI.Core/Core/Code/TypeResolver.cs @@ -33,7 +33,7 @@ private TypeDescription Resolve(MdSetup setup, ITypeSymbol type, ITypeParameterS } else { - description = new TypeDescription(symbolNames.GetDisplayName(type), ImmutableArray.Empty, typeParam); + description = new TypeDescription(symbolNames.GetGlobalName(type), ImmutableArray.Empty, typeParam); } break; @@ -73,7 +73,7 @@ private TypeDescription Resolve(MdSetup setup, ITypeSymbol type, ITypeParameterS break; default: - description = new TypeDescription(symbolNames.GetDisplayName(type), ImmutableArray.Empty, typeParam); + description = new TypeDescription(symbolNames.GetGlobalName(type), ImmutableArray.Empty, typeParam); break; } diff --git a/src/Pure.DI.Core/Core/DependencyGraphBuilder.cs b/src/Pure.DI.Core/Core/DependencyGraphBuilder.cs index 205b5e399..3f2aafe84 100644 --- a/src/Pure.DI.Core/Core/DependencyGraphBuilder.cs +++ b/src/Pure.DI.Core/Core/DependencyGraphBuilder.cs @@ -17,6 +17,7 @@ internal sealed class DependencyGraphBuilder( IFilter filter, ICache constructKinds, IRegistryManager registryManager, + ISymbolNames symbolNames, CancellationToken cancellationToken) : IDependencyGraphBuilder { @@ -269,7 +270,7 @@ public IEnumerable TryBuild( continue; } - if (injection.Type.ToString() == setup.Name.FullName) + if (symbolNames.GetName(injection.Type) == setup.Name.FullName) { // Composition var compositionBinding = CreateConstructBinding( @@ -380,12 +381,12 @@ void UpdateMap(Injection injection, DependencyNode node) private MdConstructKind GetConstructKind(INamedTypeSymbol geneticType) { var unboundGenericType = geneticType.ConstructUnboundGenericType(); - return constructKinds.Get(unboundGenericType, type => type.ToString() switch + return constructKinds.Get(unboundGenericType, type => symbolNames.GetGlobalName(type) switch { - "System.Span<>" => MdConstructKind.Span, - "System.ReadOnlySpan<>" => MdConstructKind.Span, - "System.Collections.Generic.IEnumerable<>" => MdConstructKind.Enumerable, - "System.Collections.Generic.IAsyncEnumerable<>" => MdConstructKind.AsyncEnumerable, + Names.SpanTypeName => MdConstructKind.Span, + Names.ReadOnlySpanTypeName => MdConstructKind.Span, + Names.IEnumerableTypeName => MdConstructKind.Enumerable, + Names.IAsyncEnumerableTypeName => MdConstructKind.AsyncEnumerable, _ => MdConstructKind.None }); } @@ -399,29 +400,39 @@ private bool TryCreateOnCannotResolve( IDictionary map, ISet processed) { - if (setup.Hints.IsOnCannotResolveEnabled - && filter.IsMeetRegularExpression( - setup, - (Hint.OnCannotResolveContractTypeNameRegularExpression, unresolvedInjection.Type.ToString()), - (Hint.OnCannotResolveTagRegularExpression, unresolvedInjection.Tag.ValueToString()), - (Hint.OnCannotResolveLifetimeRegularExpression, ownerNode.Lifetime.ValueToString()))) + if (setup.Hints.IsOnCannotResolveEnabled) { - var onCannotResolveBinding = CreateConstructBinding( - setup, - ownerNode, - unresolvedInjection, - unresolvedInjection.Type, - Lifetime.Transient, - ++maxId, - MdConstructKind.OnCannotResolve, - unresolvedInjection.Tag); - - var onCannotResolveNodes = CreateNodes(setup, typeConstructor, onCannotResolveBinding); - foreach (var onCannotResolveNode in onCannotResolveNodes) + var contractName = new Lazy(() => symbolNames.GetName(unresolvedInjection.Type)); + var tagName = new Lazy(() => unresolvedInjection.Tag.ValueToString()); + var lifetimeName = new Lazy(() => ownerNode.Lifetime.ValueToString()); + if (filter.IsMeetRegularExpressions( + setup, + (Hint.OnCannotResolveContractTypeNameRegularExpression, contractName), + (Hint.OnCannotResolveTagRegularExpression, tagName), + (Hint.OnCannotResolveLifetimeRegularExpression, lifetimeName)) + && filter.IsMeetWildcards( + setup, + (Hint.OnCannotResolveContractTypeNameWildcard, contractName), + (Hint.OnCannotResolveTagWildcard, tagName), + (Hint.OnCannotResolveLifetimeWildcard, lifetimeName))) { - map[unresolvedInjection] = onCannotResolveNode; - processed.Add(CreateNewProcessingNode(setup, unresolvedInjection.Tag, onCannotResolveNode)); - return true; + var onCannotResolveBinding = CreateConstructBinding( + setup, + ownerNode, + unresolvedInjection, + unresolvedInjection.Type, + Lifetime.Transient, + ++maxId, + MdConstructKind.OnCannotResolve, + unresolvedInjection.Tag); + + var onCannotResolveNodes = CreateNodes(setup, typeConstructor, onCannotResolveBinding); + foreach (var onCannotResolveNode in onCannotResolveNodes) + { + map[unresolvedInjection] = onCannotResolveNode; + processed.Add(CreateNewProcessingNode(setup, unresolvedInjection.Tag, onCannotResolveNode)); + return true; + } } } diff --git a/src/Pure.DI.Core/Core/DependencyGraphValidator.cs b/src/Pure.DI.Core/Core/DependencyGraphValidator.cs index 4be36c175..58c7e32c2 100644 --- a/src/Pure.DI.Core/Core/DependencyGraphValidator.cs +++ b/src/Pure.DI.Core/Core/DependencyGraphValidator.cs @@ -23,14 +23,24 @@ public bool Validate(DependencyGraph dependencyGraph) { cancellationToken.ThrowIfCancellationRequested(); var setup = dependencyGraph.Source; - if (setup.Hints.IsOnCannotResolveEnabled - && filter.IsMeetRegularExpression( - setup, - (Hint.OnCannotResolveContractTypeNameRegularExpression, typeResolver.Resolve(setup, unresolvedInjection.Type).Name), - (Hint.OnCannotResolveTagRegularExpression, unresolvedInjection.Tag.ValueToString()), - (Hint.OnCannotResolveLifetimeRegularExpression, dependencyNode.Lifetime.ValueToString()))) + if (setup.Hints.IsOnCannotResolveEnabled) { - continue; + var contractName = new Lazy(() => typeResolver.Resolve(setup, unresolvedInjection.Type).Name); + var tagName = new Lazy(() => unresolvedInjection.Tag.ValueToString()); + var lifetimeName = new Lazy(() => dependencyNode.Lifetime.ValueToString()); + if (filter.IsMeetRegularExpressions( + setup, + (Hint.OnCannotResolveContractTypeNameRegularExpression, contractName), + (Hint.OnCannotResolveTagRegularExpression, tagName), + (Hint.OnCannotResolveLifetimeRegularExpression, lifetimeName)) + && filter.IsMeetWildcards( + setup, + (Hint.OnCannotResolveContractTypeNameWildcard, contractName), + (Hint.OnCannotResolveTagWildcard, tagName), + (Hint.OnCannotResolveLifetimeWildcard, lifetimeName))) + { + continue; + } } isResolved = false; diff --git a/src/Pure.DI.Core/Core/Filter.cs b/src/Pure.DI.Core/Core/Filter.cs index 9a738d82f..bad7eb08b 100644 --- a/src/Pure.DI.Core/Core/Filter.cs +++ b/src/Pure.DI.Core/Core/Filter.cs @@ -6,19 +6,23 @@ namespace Pure.DI.Core; internal sealed class Filter( ILogger logger, - ICache regexCache) + ICache regexCache, + IWildcardMatcher wildcardMatcher) : IFilter { public static Regex RegexFactory(string filter) => new(filter, RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Singleline | RegexOptions.IgnoreCase); - public bool IsMeetRegularExpression(MdSetup setup, params (Hint setting, string value)[] settings) => - settings.All(i => IsMeetRegularExpression(setup, i.setting, i.value)); + public bool IsMeetRegularExpressions(MdSetup setup, params (Hint setting, Lazy textProvider)[] settings) => + settings.All(i => IsMeetRegularExpression(setup, i.setting, i.textProvider)); + + public bool IsMeetWildcards(MdSetup setup, params (Hint setting, Lazy textProvider)[] settings) => + settings.All(i => IsMeetWildcard(setup, i.setting, i.textProvider)); private bool IsMeetRegularExpression( MdSetup setup, Hint hint, - string value) + Lazy textProvider) { if (!setup.Hints.TryGetValue(hint, out var regularExpression) || string.IsNullOrWhiteSpace(regularExpression)) @@ -31,7 +35,7 @@ private bool IsMeetRegularExpression( try { var regex = regexCache.Get(regularExpression.Trim(), RegexFactory); - if (!regex.IsMatch(value)) + if (!regex.IsMatch(textProvider.Value)) { return false; } @@ -43,4 +47,32 @@ private bool IsMeetRegularExpression( return true; } + + private bool IsMeetWildcard( + MdSetup setup, + Hint hint, + Lazy textProvider) + { + if (!setup.Hints.TryGetValue(hint, out var wildcard) + || string.IsNullOrWhiteSpace(wildcard)) + { + { + return true; + } + } + + try + { + if (!wildcardMatcher.Match(wildcard.Trim().AsSpan(), textProvider.Value.AsSpan())) + { + return false; + } + } + catch (Exception ex) + { + logger.CompileError($"Invalid wildcard {wildcard}. {ex.Message}", setup.Source.GetLocation(), LogId.ErrorInvalidMetadata); + } + + return true; + } } \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/GenericTypeArguments.cs b/src/Pure.DI.Core/Core/GenericTypeArguments.cs index 2af537dea..d0327cc02 100644 --- a/src/Pure.DI.Core/Core/GenericTypeArguments.cs +++ b/src/Pure.DI.Core/Core/GenericTypeArguments.cs @@ -9,10 +9,10 @@ internal class GenericTypeArguments(ISymbolNames symbolNames) : IGenericTypeArgu private readonly ConcurrentDictionary> _genericTypeArgumentAttributesTypes = new(); public bool IsGenericTypeArgument(MdSetup setup, ITypeSymbol typeSymbol) => - _genericTypeArgumentTypes.GetOrAdd(setup, k => [..k.GenericTypeArguments.Select(i => symbolNames.GetDisplayName(i.Type))]) - .Contains(symbolNames.GetDisplayName(typeSymbol)); + _genericTypeArgumentTypes.GetOrAdd(setup, k => [..k.GenericTypeArguments.Select(i => symbolNames.GetGlobalName(i.Type))]) + .Contains(symbolNames.GetGlobalName(typeSymbol)); public bool IsGenericTypeArgumentAttribute(MdSetup setup, ITypeSymbol typeSymbol) => - _genericTypeArgumentAttributesTypes.GetOrAdd(setup, k => [..k.GenericTypeArgumentAttributes.Select(i => symbolNames.GetDisplayName(i.AttributeType))]) - .Contains(symbolNames.GetDisplayName(typeSymbol)); + _genericTypeArgumentAttributesTypes.GetOrAdd(setup, k => [..k.GenericTypeArgumentAttributes.Select(i => symbolNames.GetGlobalName(i.AttributeType))]) + .Contains(symbolNames.GetGlobalName(typeSymbol)); } \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/IFilter.cs b/src/Pure.DI.Core/Core/IFilter.cs index f4a73e60c..a338ba198 100644 --- a/src/Pure.DI.Core/Core/IFilter.cs +++ b/src/Pure.DI.Core/Core/IFilter.cs @@ -2,5 +2,7 @@ namespace Pure.DI.Core; internal interface IFilter { - bool IsMeetRegularExpression(MdSetup setup, params (Hint setting, string value)[] settings); + bool IsMeetRegularExpressions(MdSetup setup, params (Hint setting, Lazy textProvider)[] settings); + + bool IsMeetWildcards(MdSetup setup, params (Hint setting, Lazy textProvider)[] settings); } \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/ISymbolNames.cs b/src/Pure.DI.Core/Core/ISymbolNames.cs index d62b8520d..8c809e865 100644 --- a/src/Pure.DI.Core/Core/ISymbolNames.cs +++ b/src/Pure.DI.Core/Core/ISymbolNames.cs @@ -2,5 +2,7 @@ namespace Pure.DI.Core; internal interface ISymbolNames { - string GetDisplayName(ITypeSymbol typeSymbol); + string GetName(ITypeSymbol typeSymbol); + + string GetGlobalName(ITypeSymbol typeSymbol); } \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/IWildcardMatcher.cs b/src/Pure.DI.Core/Core/IWildcardMatcher.cs index 23c3ca11b..60aa38e96 100644 --- a/src/Pure.DI.Core/Core/IWildcardMatcher.cs +++ b/src/Pure.DI.Core/Core/IWildcardMatcher.cs @@ -6,13 +6,13 @@ internal interface IWildcardMatcher /// Return true if the given expression matches the given name. Supports the following wildcards: /// '*', '?', '<', '>', '"'. The backslash character '\' escapes. /// - /// The expression to match with, such as "*.foo". - /// The name to check against the expression. + /// The wildcard expression to match with, such as "*.foo". + /// The text to check against the expression. /// True to ignore case (default). /// True to use additional expressions symbols. bool Match( - ReadOnlySpan expression, - ReadOnlySpan name, + ReadOnlySpan wildcard, + ReadOnlySpan text, bool ignoreCase = false, bool useExtendedWildcards = false); } \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/MetadataSyntaxWalker.cs b/src/Pure.DI.Core/Core/MetadataSyntaxWalker.cs index 6286d3c87..57552bb7c 100644 --- a/src/Pure.DI.Core/Core/MetadataSyntaxWalker.cs +++ b/src/Pure.DI.Core/Core/MetadataSyntaxWalker.cs @@ -100,7 +100,7 @@ when memberAccess.Kind() == SyntaxKind.SimpleMemberAccessExpression if (_semanticModel?.GetTypeInfo(curInvocation) is { } typeInfo && (typeInfo.Type ?? typeInfo.ConvertedType) is { } type - && symbolNames.GetDisplayName(type) == Names.ConfigurationInterfaceName) + && symbolNames.GetGlobalName(type) == Names.IConfigurationTypeName) { return true; } diff --git a/src/Pure.DI.Core/Core/Names.cs b/src/Pure.DI.Core/Core/Names.cs index d11eb65e1..7b09c2ea9 100644 --- a/src/Pure.DI.Core/Core/Names.cs +++ b/src/Pure.DI.Core/Core/Names.cs @@ -31,23 +31,25 @@ internal static class Names public static readonly string ResolverClassName = $"Resolver{Salt}"; public const string DefaultApiMethodModifiers = "public"; public const string ParentScopeArgName = "parentScope"; - - // Interfaces public const string ResolverPropertyName = "Value"; - public const string IDisposableInterfaceName = $"{SystemNamespace}{nameof(IDisposable)}"; - public const string IAsyncDisposableInterfaceName = $"{SystemNamespace}IAsyncDisposable"; - public const string ResolverInterfaceName = $"{ApiNamespace}{nameof(IResolver)}"; - public const string ContextInterfaceName = $"{ApiNamespace}{nameof(IContext)}"; - public const string ConfigurationInterfaceName = $"{ApiNamespace}{nameof(IConfiguration)}"; // Attributes public const string OrdinalAttributeName = $"{ApiNamespace}{nameof(OrdinalAttribute)}"; public const string BindAttributeName = $"{ApiNamespace}{nameof(BindAttribute)}"; // Types + public const string IDisposableTypeName = $"{SystemNamespace}{nameof(IDisposable)}"; + public const string IAsyncDisposableTypeName = $"{SystemNamespace}IAsyncDisposable"; + public const string IResolverTypeName = $"{ApiNamespace}{nameof(IResolver)}"; + public const string IContextTypeName = $"{ApiNamespace}{nameof(IContext)}"; + public const string IConfigurationTypeName = $"{ApiNamespace}{nameof(IConfiguration)}"; public const string ObjectTypeName = $"{SystemNamespace}Object"; public const string ValueTaskTypeName = $"{SystemNamespace}Threading.Tasks.ValueTask"; public const string LockTypeName = $"{SystemNamespace}Threading.Lock"; + public const string SpanTypeName = $"{SystemNamespace}Span<>"; + public const string ReadOnlySpanTypeName = $"{SystemNamespace}ReadOnlySpan<>"; + public const string IEnumerableTypeName = $"{SystemNamespace}Collections.Generic.IEnumerable<>"; + public const string IAsyncEnumerableTypeName = $"{SystemNamespace}Collections.Generic.IAsyncEnumerable<>"; // Members public const string ResolveMethodName = nameof(IResolver.Resolve); diff --git a/src/Pure.DI.Core/Core/SetupsBuilder.cs b/src/Pure.DI.Core/Core/SetupsBuilder.cs index 7c17bc7bc..d339157d1 100644 --- a/src/Pure.DI.Core/Core/SetupsBuilder.cs +++ b/src/Pure.DI.Core/Core/SetupsBuilder.cs @@ -204,7 +204,7 @@ private void FinalizeBinding(MdSetup setup, MdBinding binding) from member in type.GetMembers() where member.DeclaredAccessibility >= Accessibility.Internal && member.CanBeReferencedByName && member is IFieldSymbol or IPropertySymbol or IMethodSymbol from attribute in member.GetAttributes() - where attribute.AttributeClass is not null && symbolNames.GetDisplayName(attribute.AttributeClass) == Names.BindAttributeName + where attribute.AttributeClass is not null && symbolNames.GetGlobalName(attribute.AttributeClass) == Names.BindAttributeName select (attribute, member); var typeConstructor = typeConstructorFactory(); @@ -346,7 +346,7 @@ from attribute in member.GetAttributes() MdResolver CreateResolver(ITypeConstructor constructor, string name, ITypeSymbol injectedType, object? tag, ref int curPosition) { - var typeSyntax = SyntaxFactory.ParseTypeName(symbolNames.GetDisplayName(injectedType)); + var typeSyntax = SyntaxFactory.ParseTypeName(symbolNames.GetGlobalName(injectedType)); if (semantic.IsValidNamespace(injectedType.ContainingNamespace)) { namespaces.Add(injectedType.ContainingNamespace.ToString()); diff --git a/src/Pure.DI.Core/Core/SymbolNames.cs b/src/Pure.DI.Core/Core/SymbolNames.cs index ee19c8d95..d0df3fd60 100644 --- a/src/Pure.DI.Core/Core/SymbolNames.cs +++ b/src/Pure.DI.Core/Core/SymbolNames.cs @@ -5,8 +5,12 @@ namespace Pure.DI.Core; internal class SymbolNames : ISymbolNames { - private readonly ConcurrentDictionary _displayNames = new(SymbolEqualityComparer.Default); - - public string GetDisplayName(ITypeSymbol typeSymbol) => - _displayNames.GetOrAdd(typeSymbol, symbol => symbol.ToDisplayString(NullableFlowState.None, SymbolDisplayFormat.FullyQualifiedFormat)); + private readonly ConcurrentDictionary _names = new(SymbolEqualityComparer.Default); + private readonly ConcurrentDictionary _globalNames = new(SymbolEqualityComparer.Default); + + public string GetName(ITypeSymbol typeSymbol) => + _names.GetOrAdd(typeSymbol, symbol => symbol.ToString()); + + public string GetGlobalName(ITypeSymbol typeSymbol) => + _globalNames.GetOrAdd(typeSymbol, symbol => symbol.ToDisplayString(NullableFlowState.None, SymbolDisplayFormat.FullyQualifiedFormat)); } \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/WildcardMatcher.cs b/src/Pure.DI.Core/Core/WildcardMatcher.cs index 764ab49d2..2b161d7d4 100644 --- a/src/Pure.DI.Core/Core/WildcardMatcher.cs +++ b/src/Pure.DI.Core/Core/WildcardMatcher.cs @@ -70,8 +70,8 @@ internal class WildcardMatcher : IWildcardMatcher // DOS_DOT matches either a . or zero characters beyond name string. public bool Match( - ReadOnlySpan expression, - ReadOnlySpan name, + ReadOnlySpan wildcard, + ReadOnlySpan text, bool ignoreCase = false, bool useExtendedWildcards = false) { @@ -79,33 +79,33 @@ public bool Match( // in the regular expression that are matching the name. When the name has been exhausted, // if one of the locations in the expression is also just exhausted, the name is in the // language defined by the regular expression. - if (expression.Length == 0 || name.Length == 0) + if (wildcard.Length == 0 || text.Length == 0) { return false; } - if (expression[0] == '*') + if (wildcard[0] == '*') { // Just * matches everything - if (expression.Length == 1) + if (wildcard.Length == 1) { return true; } - var expressionEnd = expression[1..]; + var expressionEnd = wildcard[1..]; if (expressionEnd.IndexOfAny(useExtendedWildcards ? WildcardChars : SimpleWildcardChars) == -1) { // Handle the special case of a single starting *, which essentially means "ends with" // If the name doesn't have enough characters to match the remaining expression, it can't be a match. // ReSharper disable once ConvertIfStatementToReturnStatement - if (name.Length < expressionEnd.Length) + if (text.Length < expressionEnd.Length) { return false; } // See if we end with the expression - return name.EndsWith(expressionEnd, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); + return text.EndsWith(expressionEnd, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } } @@ -120,7 +120,7 @@ public bool Match( Span priorMatches = stackalloc int[16]; priorMatches[0] = 0; - var maxState = expression.Length * 2; + var maxState = wildcard.Length * 2; int currentState; var nameFinished = false; @@ -142,14 +142,14 @@ public bool Match( // offset. Each character in the expression can represent one or two // states. * and DOS_STAR generate two states: expressionOffset * 2 and // expressionOffset * 2 + 1. All other expression characters can produce - // only a single state. Thus expressionOffset = currentState / 2. + // only a single state. Thus, expressionOffset = currentState / 2. while (!nameFinished) { - if (nameOffset < name.Length) + if (nameOffset < text.Length) { // Not at the end of the name. Grab the current character and move the offset forward. - nameChar = name[nameOffset++]; + nameChar = text[nameOffset++]; } else { @@ -172,10 +172,10 @@ public bool Match( // character of name, so we loop here until the expression stops matching. var expressionOffset = (priorMatches[priorMatch++] + 1) / 2; - while (expressionOffset < expression.Length) + while (expressionOffset < wildcard.Length) { currentState = expressionOffset * 2; - var expressionChar = expression[expressionOffset]; + var expressionChar = wildcard[expressionOffset]; // We may be about to exhaust the local space for matches, // so we have to reallocate if this is the case. @@ -207,10 +207,10 @@ public bool Match( var notLastPeriod = false; if (!nameFinished && nameChar == '.') { - for (var offset = nameOffset; offset < name.Length; offset++) + for (var offset = nameOffset; offset < text.Length; offset++) { // ReSharper disable once InvertIf - if (name[offset] == '.') + if (text[offset] == '.') { notLastPeriod = true; break; @@ -269,14 +269,14 @@ public bool Match( if (expressionChar == '\\') { // Escape character, try to move the expression forward again and match literally. - if (++expressionOffset == expression.Length) + if (++expressionOffset == wildcard.Length) { currentMatches[currentMatch++] = maxState; goto ExpressionFinished; } currentState = expressionOffset * 2 + 2; - expressionChar = expression[expressionOffset]; + expressionChar = wildcard[expressionOffset]; } // From this point on a name character is required to even @@ -311,7 +311,7 @@ public bool Match( MatchZero: currentMatches[currentMatch++] = currentState + 1; NextExpressionCharacter: - if (++expressionOffset == expression.Length) + if (++expressionOffset == wildcard.Length) { currentMatches[currentMatch++] = maxState; } diff --git a/tests/Pure.DI.UsageTests/Hints/OnCannotResolveHintScenario.cs b/tests/Pure.DI.UsageTests/Hints/OnCannotResolveRegularExpressionHintScenario.cs similarity index 94% rename from tests/Pure.DI.UsageTests/Hints/OnCannotResolveHintScenario.cs rename to tests/Pure.DI.UsageTests/Hints/OnCannotResolveRegularExpressionHintScenario.cs index 956cd9850..a475e9751 100644 --- a/tests/Pure.DI.UsageTests/Hints/OnCannotResolveHintScenario.cs +++ b/tests/Pure.DI.UsageTests/Hints/OnCannotResolveRegularExpressionHintScenario.cs @@ -1,7 +1,7 @@ /* $v=true $p=3 -$d=OnCannotResolve hint +$d=OnCannotResolve regular expression hint $h=Hints are used to fine-tune code generation. The _OnCannotResolve_ hint determines whether to generate a partial `OnCannotResolve(...)` method to handle a scenario where an instance which cannot be resolved. $h=In addition, setup hints can be comments before the _Setup_ method in the form ```hint = value```, for example: `// OnCannotResolveContractTypeNameRegularExpression = string`. $f=The `OnCannotResolveContractTypeNameRegularExpression` hint helps define the set of types that require manual dependency resolution. You can use it to specify a regular expression to filter the full type name. @@ -15,7 +15,7 @@ // ReSharper disable UnusedVariable // ReSharper disable ArrangeTypeModifiers -namespace Pure.DI.UsageTests.Hints.OnCannotResolveHintScenario; +namespace Pure.DI.UsageTests.Hints.OnCannotResolveRegularExpressionHintScenario; #pragma warning disable CA1822 using Shouldly; diff --git a/tests/Pure.DI.UsageTests/Hints/OnCannotResolveWildcardHintScenario.cs b/tests/Pure.DI.UsageTests/Hints/OnCannotResolveWildcardHintScenario.cs new file mode 100644 index 000000000..daee65e05 --- /dev/null +++ b/tests/Pure.DI.UsageTests/Hints/OnCannotResolveWildcardHintScenario.cs @@ -0,0 +1,85 @@ +/* +$v=true +$p=3 +$d=OnCannotResolve wildcard hint +$h=Hints are used to fine-tune code generation. The _OnCannotResolve_ hint determines whether to generate a partial `OnCannotResolve(...)` method to handle a scenario where an instance which cannot be resolved. +$h=In addition, setup hints can be comments before the _Setup_ method in the form ```hint = value```, for example: `// OnCannotResolveContractTypeNameWildcard = string`. +$f=The `OnCannotResolveContractTypeNameWildcard` hint helps define the set of types that require manual dependency resolution. You can use it to specify a wildcard to filter the full type name. +$f=For more hints, see [this](README.md#setup-hints) page. +$r=Shouldly +*/ + +// ReSharper disable ClassNeverInstantiated.Local +// ReSharper disable CheckNamespace +// ReSharper disable UnusedParameterInPartialMethod +// ReSharper disable UnusedVariable +// ReSharper disable ArrangeTypeModifiers + +namespace Pure.DI.UsageTests.Hints.OnCannotResolveWildcardHintScenario; +#pragma warning disable CA1822 + +using Shouldly; +using Xunit; +using static Hint; + +// { +//# using Pure.DI; +//# using static Pure.DI.Hint; +// } + +public class Scenario +{ + [Fact] + public void Run() + { + // Resolve = Off +// { + // OnCannotResolveContractTypeNameWildcard = string + DI.Setup(nameof(Composition)) + .Hint(OnCannotResolve, "On") + .Bind().To() + .Bind().To() + .Root("Root"); + + var composition = new Composition(); + var service = composition.Root; + service.Dependency.ToString().ShouldBe("My name"); + +// } + composition.SaveClassDiagram(); + } +} + +// { +interface IDependency; + +class Dependency(string name) : IDependency +{ + public override string ToString() => name; +} + +interface IService +{ + IDependency Dependency { get; } +} + +class Service(IDependency dependency) : IService +{ + public IDependency Dependency { get; } = dependency; +} + +partial class Composition +{ + private partial T OnCannotResolve( + object? tag, + Lifetime lifetime) + { + if (typeof(T) == typeof(string)) + { + return (T)(object)"My name"; + } + + throw new InvalidOperationException("Cannot resolve."); + } +} +// } \ No newline at end of file diff --git a/tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionHintScenario.cs b/tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionRegularExpressionHintScenario.cs similarity index 94% rename from tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionHintScenario.cs rename to tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionRegularExpressionHintScenario.cs index 2d42f91f8..b3e24faf7 100644 --- a/tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionHintScenario.cs +++ b/tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionRegularExpressionHintScenario.cs @@ -1,7 +1,7 @@ /* $v=true $p=2 -$d=OnDependencyInjection hint +$d=OnDependencyInjection regular expression hint $h=Hints are used to fine-tune code generation. The _OnDependencyInjection_ hint determines whether to generate partial _OnDependencyInjection_ method to control of dependency injection. $h=In addition, setup hints can be comments before the _Setup_ method in the form ```hint = value```, for example: `// OnDependencyInjection = On`. $f=The `OnDependencyInjectionContractTypeNameRegularExpression` hint helps identify the set of types that require injection control. You can use it to specify a regular expression to filter the full name of a type. @@ -18,7 +18,7 @@ // ReSharper disable UnusedMember.Global // ReSharper disable NotAccessedPositionalProperty.Global -namespace Pure.DI.UsageTests.Hints.OnDependencyInjectionHintScenario; +namespace Pure.DI.UsageTests.Hints.OnDependencyInjectionRegularExpressionHintScenario; using Shouldly; using Xunit; diff --git a/tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionWildcardHintScenario.cs b/tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionWildcardHintScenario.cs new file mode 100644 index 000000000..722f395f7 --- /dev/null +++ b/tests/Pure.DI.UsageTests/Hints/OnDependencyInjectionWildcardHintScenario.cs @@ -0,0 +1,88 @@ +/* +$v=true +$p=2 +$d=OnDependencyInjection wildcard hint +$h=Hints are used to fine-tune code generation. The _OnDependencyInjection_ hint determines whether to generate partial _OnDependencyInjection_ method to control of dependency injection. +$h=In addition, setup hints can be comments before the _Setup_ method in the form ```hint = value```, for example: `// OnDependencyInjection = On`. +$f=The `OnDependencyInjectionContractTypeNameWildcard` hint helps identify the set of types that require injection control. You can use it to specify a wildcard to filter the full name of a type. +$f=For more hints, see [this](README.md#setup-hints) page. +$r=Shouldly +*/ + +// ReSharper disable ClassNeverInstantiated.Local +// ReSharper disable CheckNamespace +// ReSharper disable UnusedParameterInPartialMethod +// ReSharper disable UnusedVariable +// ReSharper disable UnusedMemberInSuper.Global +// ReSharper disable ArrangeTypeModifiers +// ReSharper disable UnusedMember.Global + +// ReSharper disable NotAccessedPositionalProperty.Global +namespace Pure.DI.UsageTests.Hints.OnDependencyInjectionWildcardHintScenario; + +using Shouldly; +using Xunit; +using static Hint; + +// { +//# using Pure.DI; +//# using static Pure.DI.Hint; +// } + +public class Scenario +{ + [Fact] + public void Run() + { + // Resolve = Off +// { + // OnDependencyInjection = On + DI.Setup(nameof(Composition)) + .Hint(OnDependencyInjectionContractTypeNameWildcard, "*IDependency") + .RootArg("id") + .Bind().To() + .Bind().To() + .Root("GetRoot"); + + var log = new List(); + var composition = new Composition(log); + var service = composition.GetRoot(33); + + log.ShouldBe(["Dependency injected"]); +// } + composition.SaveClassDiagram(); + } +} + +// { +interface IDependency; + +record Dependency(int Id) : IDependency; + +interface IService +{ + IDependency Dependency { get; } +} + +class Service(IDependency dependency) : IService +{ + public IDependency Dependency { get; } = dependency; +} + +partial class Composition +{ + private readonly List _log = []; + + public Composition(List log) : this() => + _log = log; + + private partial T OnDependencyInjection( + in T value, + object? tag, + Lifetime lifetime) + { + _log.Add($"{value?.GetType().Name} injected"); + return value; + } +} +// } \ No newline at end of file diff --git a/tests/Pure.DI.UsageTests/Hints/OnNewInstanceHintScenario.cs b/tests/Pure.DI.UsageTests/Hints/OnNewInstanceRegularExpressionHintScenario.cs similarity index 89% rename from tests/Pure.DI.UsageTests/Hints/OnNewInstanceHintScenario.cs rename to tests/Pure.DI.UsageTests/Hints/OnNewInstanceRegularExpressionHintScenario.cs index 09afb8e6e..193ea295d 100644 --- a/tests/Pure.DI.UsageTests/Hints/OnNewInstanceHintScenario.cs +++ b/tests/Pure.DI.UsageTests/Hints/OnNewInstanceRegularExpressionHintScenario.cs @@ -1,7 +1,7 @@ /* $v=true $p=4 -$d=OnNewInstance hint +$d=OnNewInstance regular expression hint $h=Hints are used to fine-tune code generation. The _OnNewInstance_ hint determines whether to generate partial _OnNewInstance_ method. $h=In addition, setup hints can be comments before the _Setup_ method in the form ```hint = value```, for example: `// OnNewInstance = On`. $f=The `OnNewInstanceLifetimeRegularExpression` hint helps you define a set of lifetimes that require instance creation control. You can use it to specify a regular expression to filter bindings by lifetime name. @@ -17,7 +17,7 @@ // ReSharper disable ArrangeTypeModifiers // ReSharper disable UnusedMember.Global -namespace Pure.DI.UsageTests.Hints.OnNewInstanceHintScenario; +namespace Pure.DI.UsageTests.Hints.OnNewInstanceRegularExpressionHintScenario; using Shouldly; using Xunit; @@ -37,8 +37,9 @@ public void Run() // { DI.Setup(nameof(Composition)) .Hint(OnNewInstance, "On") + .Hint(OnNewInstanceLifetimeRegularExpression, "(Singleton|PerBlock)") .Bind().As(Lifetime.Singleton).To() - .Bind().To() + .Bind().As(Lifetime.PerBlock).To() .Root("Root"); var log = new List(); diff --git a/tests/Pure.DI.UsageTests/Hints/OnNewInstanceWildcardHintScenario.cs b/tests/Pure.DI.UsageTests/Hints/OnNewInstanceWildcardHintScenario.cs new file mode 100644 index 000000000..21097e6cb --- /dev/null +++ b/tests/Pure.DI.UsageTests/Hints/OnNewInstanceWildcardHintScenario.cs @@ -0,0 +1,91 @@ +/* +$v=true +$p=4 +$d=OnNewInstance wildcard hint +$h=Hints are used to fine-tune code generation. The _OnNewInstance_ hint determines whether to generate partial _OnNewInstance_ method. +$h=In addition, setup hints can be comments before the _Setup_ method in the form ```hint = value```, for example: `// OnNewInstance = On`. +$f=The `OnNewInstanceImplementationTypeNameWildcard` hint helps you define a set of implementation types that require instance creation control. You can use it to specify a wildcard to filter bindings by implementation name. +$f=For more hints, see [this](README.md#setup-hints) page. +$r=Shouldly +*/ + +// ReSharper disable ClassNeverInstantiated.Local +// ReSharper disable CheckNamespace +// ReSharper disable UnusedParameterInPartialMethod +// ReSharper disable UnusedVariable +// ReSharper disable UnusedMemberInSuper.Global +// ReSharper disable ArrangeTypeModifiers +// ReSharper disable UnusedMember.Global + +namespace Pure.DI.UsageTests.Hints.OnNewInstanceWildcardHintScenario; + +using Shouldly; +using Xunit; +using static Hint; + +// { +//# using Pure.DI; +//# using static Pure.DI.Hint; +// } + +public class Scenario +{ + [Fact] + public void Run() + { + // Resolve = Off +// { + DI.Setup(nameof(Composition)) + .Hint(OnNewInstance, "On") + .Hint(OnNewInstanceImplementationTypeNameWildcard, "*Service") + .Bind().As(Lifetime.Singleton).To() + .Bind().To() + .Root("Root"); + + var log = new List(); + var composition = new Composition(log); + var service1 = composition.Root; + var service2 = composition.Root; + + log.ShouldBe([ + "Service created", + "Service created"]); +// } + composition.SaveClassDiagram(); + } +} + +// { +interface IDependency; + +class Dependency : IDependency +{ + public override string ToString() => nameof(Dependency); +} + +interface IService +{ + IDependency Dependency { get; } +} + +class Service(IDependency dependency) : IService +{ + public IDependency Dependency { get; } = dependency; + + public override string ToString() => nameof(Service); +} + +internal partial class Composition +{ + private readonly List _log = []; + + public Composition(List log) : this() => + _log = log; + + partial void OnNewInstance( + ref T value, + object? tag, + Lifetime lifetime) => + _log.Add($"{typeof(T).Name} created"); +} +// } \ No newline at end of file