Add ability to register a type while resolving it

Closes #5
This commit is contained in:
Mike Bierlee 2016-02-03 22:55:18 +01:00
parent 7775dd3c3a
commit 9ebbb5d917
4 changed files with 336 additions and 299 deletions

View file

@ -1,84 +1,86 @@
Poodinis Changelog Poodinis Changelog
================== ==================
**Version NEXT** **Version NEXT**
* ADD setting persistent registration options * ADD setting persistent registration options
* DEPRECATE DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION, use doNotAddConcreteTypeRegistration instead * DEPRECATE DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION, use doNotAddConcreteTypeRegistration instead
* ADD resolve options to container resolve()
**Version 6.0.0** * ADD ability to register a type while resolving it. Use resolve option registerBeforeResolving
* CHANGE registration scopes are replaced by a single factory implementation. If you were not doing anything with the internal scope mechanism, you
should not be affected by this change. **Version 6.0.0**
* ADD application contexts. You can register dependencies within an application context which allow you to fine-tune the creation of dependency instances. * CHANGE registration scopes are replaced by a single factory implementation. If you were not doing anything with the internal scope mechanism, you
* CHANGE all public poodinis imports to private. This should not affect you if you use the package import "poodinis" instead of individual modules. should not be affected by this change.
* REMOVE deprecated ADD_CONCRETE_TYPE_REGISTRATION registration option. * ADD application contexts. You can register dependencies within an application context which allow you to fine-tune the creation of dependency instances.
* REMOVE deprecated RegistrationOptions alias. * CHANGE all public poodinis imports to private. This should not affect you if you use the package import "poodinis" instead of individual modules.
* REMOVE deprecated ADD_CONCRETE_TYPE_REGISTRATION registration option.
**Version 5.0.0** * REMOVE deprecated RegistrationOptions alias.
* DEPRECATE ADD_CONCRETE_TYPE_REGISTRATION registration option. It basically does nothing anymore. See next point.
* CHANGE adding registrations by super type always registers them by concrete type as well now. (Previously done with ADD_CONCRETE_TYPE_REGISTRATION). See DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION for the reverse behaviour. **Version 5.0.0**
* CHANGE RegistrationOptions enum name to RegistrationOption * DEPRECATE ADD_CONCRETE_TYPE_REGISTRATION registration option. It basically does nothing anymore. See next point.
* DEPRECATE Usage of RegistrationOptions, please use RegistrationOption instead. * CHANGE adding registrations by super type always registers them by concrete type as well now. (Previously done with ADD_CONCRETE_TYPE_REGISTRATION). See DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION for the reverse behaviour.
* CHANGE RegistrationOptions enum name to RegistrationOption
**Version 4.0.0** * DEPRECATE Usage of RegistrationOptions, please use RegistrationOption instead.
* REMOVE deprecated module "dependency.d"
**Version 4.0.0**
**Version 3.0.0** * REMOVE deprecated module "dependency.d"
This version is only compatible with DMD 2.068.0 or higher!
* ADD UDA which always resolved a new instance to an autowired member, regardless of registration scope. **Version 3.0.0**
This version is only compatible with DMD 2.068.0 or higher!
**Version 2.2.0** * ADD UDA which always resolved a new instance to an autowired member, regardless of registration scope.
* ADD canonical package module "package.d". Use "import poodinis;" to import the project.
* DEPRECATE module "dependency.d". Please use the canonical package module. See previous point. **Version 2.2.0**
* ADD autowiring of dynamic arrays. All registered instances of the element type of the array will be assigned to it. * ADD canonical package module "package.d". Use "import poodinis;" to import the project.
* DEPRECATE module "dependency.d". Please use the canonical package module. See previous point.
**Version 2.1.0** * ADD autowiring of dynamic arrays. All registered instances of the element type of the array will be assigned to it.
* ADD option for registering a class by concrete type when registering that class by supertype.
**Version 2.1.0**
**Version 2.0.0** * ADD option for registering a class by concrete type when registering that class by supertype.
This version introduces changes which might be incompatible with your current codebase
* CHANGE dependency container to be synchronized. Sharing a dependency container between threads is now possible. **Version 2.0.0**
The implication is that all dependency container instances must be shared now. This version introduces changes which might be incompatible with your current codebase
You don't have to change anything if you were only using the singleton dependency container. * CHANGE dependency container to be synchronized. Sharing a dependency container between threads is now possible.
The implication is that all dependency container instances must be shared now.
**Version 1.0.0** You don't have to change anything if you were only using the singleton dependency container.
This version introduces changes which are incompatible with previous versions
* REMOVE deprecated autowire constructor **Version 1.0.0**
* REMOVE deprecated container alias This version introduces changes which are incompatible with previous versions
* ADD documentation for public API * REMOVE deprecated autowire constructor
* REMOVE @Autowired UDA. Use @Autowire instead. * REMOVE deprecated container alias
* ADD quickstart from readme to compilable example project. * ADD documentation for public API
* ADD example project for the use of qualifiers * REMOVE @Autowired UDA. Use @Autowire instead.
* ADD quickstart from readme to compilable example project.
**Version 0.3.1** * ADD example project for the use of qualifiers
* FIX issue where autowiring members which are declared by interface or supertype would get autowired incorrectly.
**Version 0.3.1**
**Version 0.3.0** * FIX issue where autowiring members which are declared by interface or supertype would get autowired incorrectly.
* ADD alternative workaround to readme for autowire limitation
* CHANGE returning of resolved instances by returning them by qualifier type instead **Version 0.3.0**
* ADD debug specifier to reduce verbosity of debug output * ADD alternative workaround to readme for autowire limitation
* CHANGE returning of resolved instances by returning them by qualifier type instead
**Version 0.2.0** * ADD debug specifier to reduce verbosity of debug output
* ADD ability to register type with multiple concrete types. They can be correctly resolved using qualifiers.
* DEPRECATE template for autowiring in constructor. This workaround is buggy. Use qualifiers instead. **Version 0.2.0**
* ADD ability to register type with multiple concrete types. They can be correctly resolved using qualifiers.
**Version 0.1.4** * DEPRECATE template for autowiring in constructor. This workaround is buggy. Use qualifiers instead.
* Make Poodinis compatible with D 2.066.0 and DUB 0.9.22
* FIX incorrect clearing of registrations **Version 0.1.4**
This release should be backwards compatible with the previous versions of D and DUB, but please note that there are no more separate * Make Poodinis compatible with D 2.066.0 and DUB 0.9.22
configurations for release and debug builds. You have to specify a build type in DUB. * FIX incorrect clearing of registrations
This release should be backwards compatible with the previous versions of D and DUB, but please note that there are no more separate
**Version 0.1.3** configurations for release and debug builds. You have to specify a build type in DUB.
* ADD global autowire function for convenience
* CHANGE workaround to be more simple **Version 0.1.3**
* FIX autowiring classes which contain non-symbolic declarations such as aliases. As a result, only variables are attempted to be autowired. * ADD global autowire function for convenience
* CHANGE workaround to be more simple
**Version 0.1.2** * FIX autowiring classes which contain non-symbolic declarations such as aliases. As a result, only variables are attempted to be autowired.
* ADD workaround for failing to autowire types registered by supertype or interface
**Version 0.1.2**
**Version 0.1.1** * ADD workaround for failing to autowire types registered by supertype or interface
* FIX: Also auto-wire members from base classes
**Version 0.1.1**
**Version 0.1.0** * FIX: Also auto-wire members from base classes
* Initial open-source release
* ADD support for registering and resolving **Version 0.1.0**
* ADD registration scopes * Initial open-source release
* ADD autowiring * ADD support for registering and resolving
* ADD registration scopes
* ADD autowiring

View file

@ -1,207 +1,212 @@
Poodinis Tutorial Poodinis Tutorial
================= =================
This tutorial will give you an overview of all functionality offered by Poodinis and how to use them. This tutorial will give you an overview of all functionality offered by Poodinis and how to use them.
The container The container
------------- -------------
To register a class, a new dependency container must be instantiated: To register a class, a new dependency container must be instantiated:
```d ```d
// Register a private container // Register a private container
auto dependencies = new DependencyContainer(); auto dependencies = new DependencyContainer();
// Or use the singleton container // Or use the singleton container
dependencies = DependencyContainer.getInstance(); dependencies = DependencyContainer.getInstance();
``` ```
###Registering dependencies ###Registering dependencies
To make dependencies available, they have to be registered: To make dependencies available, they have to be registered:
```d ```d
// Register concrete class // Register concrete class
dependencies.register!ExampleClass; dependencies.register!ExampleClass;
// Register by super type // Register by super type
dependencies.register!(ExampleInterface, ExampleClass); dependencies.register!(ExampleInterface, ExampleClass);
``` ```
In the above example, dependencies on the concrete class and interface will resolve an instance of class ExampleClass. A dependency registered by super type will automatically be registered by concrete type. In the above example, dependencies on the concrete class and interface will resolve an instance of class ExampleClass. A dependency registered by super type will automatically be registered by concrete type.
Resolving dependencies Resolving dependencies
---------------------- ----------------------
To manually resolve a dependency, all you have to do is resolve the dependency's type using the container in which it is registered: To manually resolve a dependency, all you have to do is resolve the dependency's type using the container in which it is registered:
```d ```d
auto exampleClassInstance = dependencies.resolve!ExampleClass; auto exampleClassInstance = dependencies.resolve!ExampleClass;
``` ```
If the class is registered by interface and not by concrete type, you can still resolve the class by concrete type: If the class is registered by interface and not by concrete type, you can still resolve the class by concrete type:
```d ```d
auto exampleClassInstance = dependencies.resolve!ExampleInterface; auto exampleClassInstance = dependencies.resolve!ExampleInterface;
auto exampleClassInstance2 = dependencies.resolve!ExampleClass; auto exampleClassInstance2 = dependencies.resolve!ExampleClass;
assert(exampleClassInstance is exampleClassInstance2); assert(exampleClassInstance is exampleClassInstance2);
``` ```
If you want to prevent registrations from being both registered by interface and concrete type, use the DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION option when registering: If you want to prevent registrations from being both registered by interface and concrete type, use the DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION option when registering:
```d ```d
dependencies.register!(ExampleInterface, ExampleClass)(RegistrationOptions.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION); dependencies.register!(ExampleInterface, ExampleClass)(RegistrationOptions.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION);
auto exampleClassInstance = dependencies.resolve!ExampleInterface; auto exampleClassInstance = dependencies.resolve!ExampleInterface;
auto exampleClassInstance2 = dependencies.resolve!ExampleClass; // A ResolveException is thrown auto exampleClassInstance2 = dependencies.resolve!ExampleClass; // A ResolveException is thrown
``` ```
It is also possible to register a type while resolving it. Doing so means you don't need to explicitly register it beforehand. To do this, use the resolve option "registerBeforeResolving":
Dependency creation behaviour ```d
----------------- dependencies.resolve!ExampleClass([ResolveOption.registerBeforeResolving]);
You can control how a dependency is resolved by specifying a creation scope during registration. The scope determines which instance is returned, be it the same each time or a new one. The following scopes are available: ```
Naturally this can only be done when you are resolving a concrete type or an interface type by qualifier.
* Resolve a dependency using a single instance (default):
Dependency creation behaviour
```d -----------------
dependencies.register!(ExampleClass).singleInstance(); You can control how a dependency is resolved by specifying a creation scope during registration. The scope determines which instance is returned, be it the same each time or a new one. The following scopes are available:
```
* Resolve a dependency with a new instance each time it is resolved: * Resolve a dependency using a single instance (default):
```d ```d
dependencies.register!(ExampleClass).newInstance(); dependencies.register!(ExampleClass).singleInstance();
``` ```
* Resolve a dependency using a pre-existing instance * Resolve a dependency with a new instance each time it is resolved:
```d ```d
auto preExistingInstance = new ExampleClass(); dependencies.register!(ExampleClass).newInstance();
dependencies.register!(ExampleClass).existingInstance(preExistingInstance); ```
``` * Resolve a dependency using a pre-existing instance
Autowiring ```d
---------- auto preExistingInstance = new ExampleClass();
The real value of any dependency injection framework comes from its ability to autowire dependencies. Poodinis supports autowiring by simply applying the **@Autowire** UDA to a member of a class: dependencies.register!(ExampleClass).existingInstance(preExistingInstance);
```d ```
class ExampleClassA {}
Autowiring
class ExampleClassB { ----------
@Autowire The real value of any dependency injection framework comes from its ability to autowire dependencies. Poodinis supports autowiring by simply applying the **@Autowire** UDA to a member of a class:
public ExampleClassA dependency; ```d
} class ExampleClassA {}
dependencies.register!ExampleClassA; class ExampleClassB {
auto exampleInstance = new ExampleClassB(); @Autowire
dependencies.autowire(exampleInstance); public ExampleClassA dependency;
assert(exampleInstance.dependency !is null); }
```
At the moment, it is only possible to autowire public members or properties. dependencies.register!ExampleClassA;
auto exampleInstance = new ExampleClassB();
Dependencies are automatically autowired when a class is resolved. So when you register ExampleClassB, its member, *dependency*, is automatically autowired: dependencies.autowire(exampleInstance);
```d assert(exampleInstance.dependency !is null);
dependencies.register!ExampleClassA; ```
dependencies.register!ExampleClassB; At the moment, it is only possible to autowire public members or properties.
auto instance = dependencies.resolve!ExampleClassB;
assert(instance.dependency !is null); Dependencies are automatically autowired when a class is resolved. So when you register ExampleClassB, its member, *dependency*, is automatically autowired:
``` ```d
If an interface is to be autowired, you must register a concrete class by interface. Any class registered by concrete type can only be injected when a dependency on a concrete type is autowired. dependencies.register!ExampleClassA;
dependencies.register!ExampleClassB;
Circular dependencies auto instance = dependencies.resolve!ExampleClassB;
--------------------- assert(instance.dependency !is null);
Poodinis can autowire circular dependencies when they are registered with singleInstance or existingInstance registration scopes. Circular dependencies in registrations with newInstance scopes will not be autowired, as this would cause an endless loop. ```
If an interface is to be autowired, you must register a concrete class by interface. Any class registered by concrete type can only be injected when a dependency on a concrete type is autowired.
Registering and resolving using qualifiers
------------------------------------------ Circular dependencies
You can register multiple concrete types to a super type. When doing so, you will need to specify a qualifier when resolving that type: ---------------------
```d Poodinis can autowire circular dependencies when they are registered with singleInstance or existingInstance registration scopes. Circular dependencies in registrations with newInstance scopes will not be autowired, as this would cause an endless loop.
// Color is an interface, Blue and Red are classes implementing that interface
dependencies.register!(Color, Blue); Registering and resolving using qualifiers
dependencies.register!(Color, Red); ------------------------------------------
auto blueInstance = dependencies.resolve!(Color, Blue); You can register multiple concrete types to a super type. When doing so, you will need to specify a qualifier when resolving that type:
``` ```d
If you want to autowire a type registered to multiple concrete types, specify a qualified type as template argument: // Color is an interface, Blue and Red are classes implementing that interface
```d dependencies.register!(Color, Blue);
class BluePaint { dependencies.register!(Color, Red);
@Autowire!Blue auto blueInstance = dependencies.resolve!(Color, Blue);
public Color color; ```
} If you want to autowire a type registered to multiple concrete types, specify a qualified type as template argument:
``` ```d
If you registered multiple concrete types to the same supertype and you do not resolve using a qualifier, a ResolveException is thrown stating that there are multiple candidates for the type to be resolved. class BluePaint {
@Autowire!Blue
Autowiring all registered instances to an array public Color color;
----------------------------------------------- }
If you have registered multiple concrete types to a super type, you can autowire them all to an array, in which case you can easily operate on them all: ```
```d If you registered multiple concrete types to the same supertype and you do not resolve using a qualifier, a ResolveException is thrown stating that there are multiple candidates for the type to be resolved.
// Color is an interface, Blue and Red are classes implementing that interface
Autowiring all registered instances to an array
class ColorMixer { -----------------------------------------------
@Autowire If you have registered multiple concrete types to a super type, you can autowire them all to an array, in which case you can easily operate on them all:
public Color[] colors; ```d
} // Color is an interface, Blue and Red are classes implementing that interface
dependencies.register!(Color, Blue); class ColorMixer {
dependencies.register!(Color, Red); @Autowire
auto mixer = dependencies.resolve!ColorMixer; public Color[] colors;
``` }
Member mixer.colors will now contain instances of Blue and Red. The order of the instances is not guarenteed to be that of the order in which they were registered.
dependencies.register!(Color, Blue);
Application Contexts dependencies.register!(Color, Red);
-------------------- auto mixer = dependencies.resolve!ColorMixer;
You can fine-tune dependency configuration using application contexts. Application contexts allow you to centralize all dependency configuration as well as define ```
how instances of certain classes should be constructed using factory methods. Member mixer.colors will now contain instances of Blue and Red. The order of the instances is not guarenteed to be that of the order in which they were registered.
###Defining and using application contexts
An application context is defined as follows: Application Contexts
```d --------------------
class Context : ApplicationContext { You can fine-tune dependency configuration using application contexts. Application contexts allow you to centralize all dependency configuration as well as define
public override void registerDependencies(shared(DependencyContainer) container) { how instances of certain classes should be constructed using factory methods.
container.register!SomeClass; ###Defining and using application contexts
container.register!(SomeInterface, SomeOtherClass).newInstance(); An application context is defined as follows:
} ```d
class Context : ApplicationContext {
@Component public override void registerDependencies(shared(DependencyContainer) container) {
public SomeLibraryClass libraryClass() { container.register!SomeClass;
return new SomeLibraryClass("This class needs constructor parameters so I have to register it through an application context"); container.register!(SomeInterface, SomeOtherClass).newInstance();
} }
}
``` @Component
In the override *registerDependencies()* you can register all dependencies which do not need complex set-up, just like you would do when directly using the dependency container. public SomeLibraryClass libraryClass() {
This override is optional. You can still register simple dependencies outside of the context (or in another context). return new SomeLibraryClass("This class needs constructor parameters so I have to register it through an application context");
Complex dependencies are registered through member methods of the context. These member methods serve as factory methods which will be called when a dependency is resolved. }
They are annotated with the *@Component* UDA to let the container know that these methods should be registered as dependencies. The type of the registration is the same as the return type of the method. }
Factory methods are useful when you have to deal with dependencies which require constructor arguments or elaborate set-up after instantiation. ```
Application contexts have to be registered with a dependency container. They are registered as follows: In the override *registerDependencies()* you can register all dependencies which do not need complex set-up, just like you would do when directly using the dependency container.
```d This override is optional. You can still register simple dependencies outside of the context (or in another context).
container.registerContext!Context; Complex dependencies are registered through member methods of the context. These member methods serve as factory methods which will be called when a dependency is resolved.
``` They are annotated with the *@Component* UDA to let the container know that these methods should be registered as dependencies. The type of the registration is the same as the return type of the method.
All registered dependencies can now be resolved by the same dependency container. Registering a context will also register it as a dependency, meaning you can autowire the application context in other classes. Factory methods are useful when you have to deal with dependencies which require constructor arguments or elaborate set-up after instantiation.
You can register as many types of application contexts as you like. Application contexts have to be registered with a dependency container. They are registered as follows:
```d
###Autowiring application contexts container.registerContext!Context;
Application contexts can make use of autowired dependencies like any other dependency. When registering an application context, all its components are registered first after which the application context is autowired. ```
This means that after the registration of an application context some dependencies will already be resolved and instantiated. The following example illustrates how autowired members can be used in a context: All registered dependencies can now be resolved by the same dependency container. Registering a context will also register it as a dependency, meaning you can autowire the application context in other classes.
```d You can register as many types of application contexts as you like.
class Context : ApplicationContext {
###Autowiring application contexts
@Autowire Application contexts can make use of autowired dependencies like any other dependency. When registering an application context, all its components are registered first after which the application context is autowired.
public SomeClass someClass; This means that after the registration of an application context some dependencies will already be resolved and instantiated. The following example illustrates how autowired members can be used in a context:
```d
@Autowire class Context : ApplicationContext {
public SomeOtherClass someOtherClass;
@Autowire
public override void registerDependencies(shared(DependencyContainer) container) { public SomeClass someClass;
container.register!SomeClass;
} @Autowire
public SomeOtherClass someOtherClass;
@Component
public SomeLibraryClass libraryClass() { public override void registerDependencies(shared(DependencyContainer) container) {
return new SomeLibraryClass(someClass, someOtherClass); container.register!SomeClass;
} }
}
``` @Component
As you can see, autowired dependencies can be used within factory methods. When *SomeLibraryClass* is resolved, it will be created with a resolved instance of *SomeClass* and *SomeOtherClass*. As shown, autowired dependencies can be registered within the same public SomeLibraryClass libraryClass() {
application context, but don't neccesarily have to be. You can even autowire dependencies which are created within a factory method within the same application context. return new SomeLibraryClass(someClass, someOtherClass);
Application contexts are directly autowired after they have been registered. This means that all autowired dependencies which are not registered in the application context itself need to be registered before registering the application context. }
}
###Controlling component registration ```
You can further influence how components are registered and created with additional UDAs: As you can see, autowired dependencies can be used within factory methods. When *SomeLibraryClass* is resolved, it will be created with a resolved instance of *SomeClass* and *SomeOtherClass*. As shown, autowired dependencies can be registered within the same
```d application context, but don't neccesarily have to be. You can even autowire dependencies which are created within a factory method within the same application context.
class Context : ApplicationContext { Application contexts are directly autowired after they have been registered. This means that all autowired dependencies which are not registered in the application context itself need to be registered before registering the application context.
@Component
@Prototype // Will create a new instance every time the dependency is resolved. ###Controlling component registration
@RegisterByType!SomeInterface // Registers the dependency by the specified super type instead of the return type You can further influence how components are registered and created with additional UDAs:
public SomeClass someClass() { ```d
return new SomeClass(); class Context : ApplicationContext {
} @Component
} @Prototype // Will create a new instance every time the dependency is resolved.
``` @RegisterByType!SomeInterface // Registers the dependency by the specified super type instead of the return type
public SomeClass someClass() {
Persistent Registration Options return new SomeClass();
------------------------------- }
If you want registration options to be persistent (applicable for every call to register()), you can use the container method setPersistentRegistrationOptions(): }
```d ```
dependencies.setPersistentRegistrationOptions(RegistrationOption.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION); // Sets the option
dependencies.unsetPersistentRegistrationOptions(); // Clears the persistentent options Persistent Registration Options
-------------------------------
If you want registration options to be persistent (applicable for every call to register()), you can use the container method setPersistentRegistrationOptions():
```d
dependencies.setPersistentRegistrationOptions(RegistrationOption.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION); // Sets the option
dependencies.unsetPersistentRegistrationOptions(); // Clears the persistentent options
``` ```

View file

@ -61,6 +61,18 @@ public enum RegistrationOption {
DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION
} }
/**
* Options which influence the process of resolving dependencies
*/
public enum ResolveOption {
/**
* Registers the type you're trying to resolve before returning it.
* This essentially makes registration optional for resolving by concerete types.
* Resolinvg will still fail when trying to resolve a dependency by supertype.
*/
registerBeforeResolving
}
/** /**
* The dependency container maintains all dependencies registered with it. * The dependency container maintains all dependencies registered with it.
* *
@ -77,6 +89,7 @@ synchronized class DependencyContainer {
private Registration[] autowireStack; private Registration[] autowireStack;
private RegistrationOption[] persistentRegistrationOptions; private RegistrationOption[] persistentRegistrationOptions;
private ResolveOption[] persistentResolveOptions;
/** /**
* Register a dependency by concrete class type. * Register a dependency by concrete class type.
@ -155,9 +168,11 @@ synchronized class DependencyContainer {
private bool hasOption(OptionType)(OptionType[] options, shared(OptionType[]) persistentOptions, OptionType option) { private bool hasOption(OptionType)(OptionType[] options, shared(OptionType[]) persistentOptions, OptionType option) {
foreach (presentOption; persistentOptions) { foreach (presentOption; persistentOptions) {
// DEPRECATED LEGACY COMPATIBILITY - REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE (SOON) static if (is(OptionType == RegistrationOption)) {
if (presentOption == RegistrationOption.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION) { // DEPRECATED LEGACY COMPATIBILITY - REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE (SOON)
presentOption = RegistrationOption.doNotAddConcreteTypeRegistration; if (presentOption == RegistrationOption.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION) {
presentOption = RegistrationOption.doNotAddConcreteTypeRegistration;
}
} }
if (presentOption == option) { if (presentOption == option) {
@ -166,9 +181,11 @@ synchronized class DependencyContainer {
} }
foreach(presentOption ; options) { foreach(presentOption ; options) {
// DEPRECATED LEGACY COMPATIBILITY - REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE (SOON) static if (is(OptionType == RegistrationOption)) {
if (presentOption == RegistrationOption.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION) { // DEPRECATED LEGACY COMPATIBILITY - REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE (SOON)
presentOption = RegistrationOption.doNotAddConcreteTypeRegistration; if (presentOption == RegistrationOption.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION) {
presentOption = RegistrationOption.doNotAddConcreteTypeRegistration;
}
} }
if (presentOption == option) { if (presentOption == option) {
@ -237,8 +254,8 @@ synchronized class DependencyContainer {
* --- * ---
* You need to use the resolve method which allows you to specify a qualifier. * You need to use the resolve method which allows you to specify a qualifier.
*/ */
public RegistrationType resolve(RegistrationType)() { public RegistrationType resolve(RegistrationType)(ResolveOption[] resolveOptions = []) {
return resolve!(RegistrationType, RegistrationType)(); return resolve!(RegistrationType, RegistrationType)(resolveOptions);
} }
/** /**
@ -267,7 +284,7 @@ synchronized class DependencyContainer {
* container.resolve!(Animal, Dog); * container.resolve!(Animal, Dog);
* --- * ---
*/ */
public QualifierType resolve(RegistrationType, QualifierType : RegistrationType)() { public QualifierType resolve(RegistrationType, QualifierType : RegistrationType)(ResolveOption[] resolveOptions = []) {
TypeInfo resolveType = typeid(RegistrationType); TypeInfo resolveType = typeid(RegistrationType);
TypeInfo qualifierType = typeid(QualifierType); TypeInfo qualifierType = typeid(QualifierType);
@ -275,6 +292,12 @@ synchronized class DependencyContainer {
writeln("DEBUG: Resolving type " ~ resolveType.toString() ~ " with qualifier " ~ qualifierType.toString()); writeln("DEBUG: Resolving type " ~ resolveType.toString() ~ " with qualifier " ~ qualifierType.toString());
} }
static if (__traits(compiles, new QualifierType())) {
if (hasOption(resolveOptions, persistentResolveOptions, ResolveOption.registerBeforeResolving)) {
register!(RegistrationType, QualifierType)();
}
}
auto candidates = resolveType in registrations; auto candidates = resolveType in registrations;
if (!candidates) { if (!candidates) {
throw new ResolveException("Type not registered.", resolveType); throw new ResolveException("Type not registered.", resolveType);

View file

@ -627,4 +627,11 @@ version(unittest) {
container.register!(TestInterface, TestClass); container.register!(TestInterface, TestClass);
container.resolve!TestClass; container.resolve!TestClass;
} }
// Test registration when resolving
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.resolve!(TestInterface, TestClass)([ResolveOption.registerBeforeResolving]);
container.resolve!TestClass;
}
} }