-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Verification Pattern
Tested Member Types
- Field
- Property
- Parameter
Types of Injection
Unity container supports three different types of dependency injection:
- Implicit dependencies
- Annotated dependencies
- Injected dependencies
Implicit Dependencies
Dependency is implicit if no injection directives such as InjectionMember or DependencyAttribute are present but the member is still required to be injected. An example of implicit dependency would be a constructor's and a method's parameters:
public class PocoType
{
public int Field;
public int Property { get; set; }
[InjectionConstructor]
public NoDefault_Value(int value) { }
[InjectionMethod]
public void Method(int value) { }
}Although parameter value is not explicitly annotated, the method and the constructor are both annotated with injection attribute, so the corresponding parameters are implicitly marked for injection.
With Defaults
All members, Fields, Properties, and Parameters could have default value assigned to it at design time:
public class PocoTypeWithDefaults
{
public int Field = 111;
public int Property { get; set; } = 222;
[InjectionConstructor]
public NoDefault_Value(int value = 333) { }
[InjectionMethod]
public void Method(int value = 444) { }
}When resolving such types, in case when value could not be resolved, it should fall back to provided default value. The parameter value should be assigned the default (333 or 444 respectively).
If resolution is successful members should be assigned the resolved value.
Test Pattern
Unity tests both cases, where dependencies are registered and when container is empty. This leads to the following test cases:
- Unregistered_Implicit
- Unregistered_Implicit_WithDefault
- Registered_Implicit
- Registered_Implicit_WithDefault
Annotated Dependencies
Dependency injection could be specified by annotating members with attributes. There are two types of dependencies:
- Required dependencies
- Optional dependencies
Required Dependencies
Required dependencies are annotated with DependencyAttribute
public class TypeWithRequiredDependency
{
[Dependency]
public int Field;
[Dependency]
public int Property { get; set; }
[InjectionConstructor]
public TypeWithRequiredDependency([Dependency] int value) { }
[InjectionMethod]
public void Method([Dependency] int value) { }
}Resolution of required dependencies is different for Parameters compared with Fields and Properties.
Required Parameters
Based on availability of default value, when resolving it, Unity will throw if no default value is available or fall back on default value if it exists. So the following will throw if can not be resolved:
public class TypeWithRequiredDependency
{
[InjectionConstructor]
public TypeWithRequiredDependency([Dependency] int value) { }
[InjectionMethod]
public void Method([Dependency] int value) { }
}this will never throw:
public class TypeWithDefaults
{
[InjectionConstructor]
public TypeWithDefaults([Dependency] int value = 22) { }
[InjectionMethod]
public void Method([Dependency] int value = 33) { }
}Required Properties and Fields
Properties and Fields annotated with DependencyAttribute will always throw if value could not be resolved. The following example will throw if int is not registered:
public class TypeWithRequiredDependency
{
[Dependency]
public int Field = 33;
[Dependency]
public int Property { get; set; }
}Test Pattern
Unity tests both cases, where dependencies are registered and when container is empty. This leads to the following test cases:
- Unregistered_Required
- Unregistered_Required_WithDefault
- Registered_Required
- Registered_Required_WithDefault
Optional Dependencies
Optional dependencies are annotated with OptionalDependencyAttribute.
public class TypeWithOptionalDependency
{
[OptionalDependency]
public int Field = 11;
[OptionalDependency]
public int Property { get; set; }
[InjectionConstructor]
public TypeWithOptionalDependency([OptionalDependency] int value) { }
[InjectionMethod]
public void Method([OptionalDependency] int value = 33) { }
}Unity attempts to inject Optional parameters with resolved values but instead of throwing if it can't satisfy dependency, it just falls back on default. Resolution of Optional dependencies is slightly different for Parameters and Fields or Properties.
Optional Parameters
Optional parameters are initialized as follows:
- Resolved value, if available
DefaultValueis availabledefaultfor the Type
Resolving Optional dependencies never throws.
Optional Properties and Fields
Properties and Fields annotated for Optional injection are inizialized as follows:
- Resolved value, if available
- If can't resolve, leave the member as is
When optional dependency can not be satisfied by the container, the value already assigned to it is stay the same. This is done so to allow design time initialization:
public class TypeWithOptionalDependency
{
[OptionalDependency]
public int Field = 11;
[OptionalDependency]
public int Property { get; set; }
}The logic behind this design decision is as follows:
-
For the
Property, in case dependency resolution failed, initializing it withnullis redundant (it is alreadynull). But if property is initialized with initial value, it will override that value and data will be lost. -
Fieldis initialized with default value. Assigningdefault(value of 0) in case of resolution failure is incorrect. It should be left as11.
Test Pattern
Unity tests both cases, where dependencies are registered and when container is empty. This leads to the following test cases:
- Unregistered_Optional
- Unregistered_Optional_WithDefault
- Registered_Optional
- Registered_Optional_WithDefault
Injected Dependencies
Unity uses InjectionMember derived types to specify injection targets on classes. For example ths following type
public class PocoType
{
public int Field;
public int Property { get; set; }
public PocoType(int value) { }
public void Method(int value) { }
public void Method([Dependency(name)]string value) { }
public void Method([OptionalDependency]long value) { }
public void Method(IUnityContainer value) { }
}is not annotated for injection.
To specify what to inject a type must be registered with the container and provide an InjectionMember for each field, property, method, or constructor that must be injected.
Unity recognizes three types of injection:
- Implicit injection
- Explicit injection
Implicit Injection
Implicit injection is only applicable to methods and constructors.
It is possible to instruct the container to invoke specific constructor or a method by specifying the signature without providing any injection info for corresponding parameters. For example, a registration like this would instruct the container to invoke a method Method(int value):
Container.RegisterType(type, new InjectionConstructor(typeof(int)))
.RegisterType(type, new InjectionMethod("Method", typeof(int)));With no injection configured for each parameter, the container will fall back to rules for implicit dependencies:
- if parameter not annotated, it considered required dependency
- if parameter is annotated, it will be resolved according to annotation
Explicit injection
Injection is explicit when exact directions are given on how to resolve dependency. For example, to be explicit, the same registration could be rewritten like this:
Container.RegisterType(type, new InjectionConstructor(new InjectionParameter(11))))
.RegisterType(type, new InjectionMethod("Method", new InjectionParameter(22)));Test Pattern
Unity tests both cases, where dependencies are registered and when container is empty. This leads to the following test cases:
Dependency Resolution
- Implicit
- No Default
- Value Type ✔️
- Class ✔️
- Default Value
- Value Type ✔️
- Class ✔️
- No Default
- Annotated
- Required ( Anonymous / Named )
- No Default
- ValueType
- Class
- Generic
- Default Value
- ValueType
- Class
- Generic
- No Default
- Optional ( Anonymous / Named )
- No Default
- ValueType
- Class
- Generic
- Default Value
- ValueType
- Class
- Generic
- No Default
- Required ( Anonymous / Named )
- Injected
- Required ( Anonymous / Named )
- No Default
- ValueType
- Class
- Generic
- Default Value
- ValueType
- Class
- Generic
- No Default
- Optional ( Anonymous / Named )
- No Default
- ValueType
- Class
- Generic
- Default Value
- ValueType
- Class
- Generic
- No Default
- Required ( Anonymous / Named )
Tested Variants
- Dependency Type (None, Annotated, Injected)
- No Default
- Registered
- ValueType
- Class
- Generic
- Missing
- ValueType
- Class
- Generic
- Registered
- Default Value
- Registered
- ValueType
- Class
- Generic
- Missing
- ValueType
- Class
- Generic
- Registered
- No Default
Parameter Injection Pattern
- Injected implicitly
- Injected with member Type identifier