-
Notifications
You must be signed in to change notification settings - Fork 17
Configuration
Compile time configuration (assembly attributes)
- Generate Debuggable Code
- Generate Registrations
- Included Assemblies
- Custom Mapping
- Object Lifetime
- Behaviors
- Initialization Mode
- Debug Log Level
- Debug Exceptions
- Constructor Selection
- Property Injection
For AutoDI 3.x configuration see legacy configuration
Run time configuration ([SetupMethodAttribute]
)
The following configurations are all specified with assembly attributes. These configurations are processed in order, so if there are rules that match the same type, the last one wins.
Default: None
[assembly:AutoDI.Settings(DebugCodeGeneration = AutoDI.CodeLanguage.CSharp)]
AutoDI can generate debuggable code to allow you to see the dependency parameters get resolved. This feature is off by default. It will also generate reference code for the initialization code. The code that it generates should be considered reference code. This code was not compiled to produce the final output, rather it is a reflection of the IL that was generated.
Default: True
[assembly:AutoDI.Settings(GenerateRegistrations = false)]
When this is true, AutoDI will construct a mapping of all types and build up all of the dependency registrations. These registrations are added at run time when AutoDI is initialized.
Default: none
[assembly:AutoDI.IncludeAssembly("MyCompany.*")]
AutoDI will always scan the assembly being processed (ie. the assembly with the AutoDI.Build NuGet package installed) when building up the list of types to register. If there are additional dependent assemblies that AutoDI should also include you can specify them with an IncludeAssembly
assembly attribute. The constructor parameter (assemblyPattern
) follows the same matching rules at the Map (below) to match the assembly's full name.
Default: none
[assembly:AutoDI.Map(".I*", ".*", Lifetime.Singleton)]
[assembly:AutoDI.Map(@"regex:Interfaces\.I(.*)", @"regex:Services\.$1", Force = false)]
If you need to configure a specific mapping between types you can specify it with an [AutoDI.MapAttribute]
specifying the SourceTypePattern
and TargetTypePattern
. Both of these may contain a wildcard (*). In the first example [assembly:AutoDI.Map(".I*", ".*", Lifetime.Singleton)]
maps all types that start with an 'I' to their corresponding type without the 'I' prefix. Note that the .
in this example are being used to match a namespace delimiter and are not regular expressions. A wildcard in the TargetTypePattern
will be replaced with the matched wildcard value in the SourceTypePattern
attribute.
The SourceTypePattern
and TargetTypePattern
can also contain regular expressions to allow for more complex matching scenarios. For these, prefix the value with "regex:" (as seen above). The pattern in the TargetTypePattern
attribute may contain regex groups that were matched in the SourceTypePattern
attribute. This allows for some more complex mappings to be declared.
Maps may also specify a Lifetime
to use for all of the mappings that are generated from it. This lifetime may be overridden by subsequent maps. Mapping that do not have an explicit lifetime will use LazySingleton
.
AutoDI will also verify that the target type (specified in the TargetTypePattern
attribute) can be cast to the service type (specified in the SourceTypePattern
attribute). If the service type is not in the target type's hierarchy it will ignore the mapping. You can disable this check by setting the Force
property to true (default is false).
When matching on nested types you may use either '/' or '+' to denote a nested class.
[assembly:AutoDI.Map("Container+*", "Container/*")]
Default: none
[assembly:AutoDI.Map(@"regex:Namespace\..+Service", Lifetime.Transient)]
Unless otherwise specified, the generated registrations by AutoDI have a lifetime of LazySingleton (see below). You can change the lifetime used for generated registration with the MapAttribute
. The TargetTypePattern
is processed using the same matching rules as above. All generated registrations whose target type's full name matches this pattern will use the specified lifetime.
AutoDI supports the following lifetimes:
- LazySingleton (Default) - A singleton instance. The object is not created until the first time it is requested.
- Singleton - A singleton instance. The object is created when AutoDI is instantiated.
- Scoped - A unique lazy singleton instances is created in each service scope.
- WeakSingleton - A single instance is created and returned. The container holds a weak reference to the instance and will return the same instance as long as it is alive; otherwise a new instance is created. In version 3.0.0 this was named WeakTransient (#61)
- Transient - A new instance is created for each request.
- None - Used to un-register a type that may have been previously registered.
You can also remove types that were previously mapped by simply setting the Lifetime
attribute to None.
The following removes all types within the Exclude namespace.
[assembly:AutoDI.Map("Exclude.*", Lifetime.None)]
Default: Behaviors.Default
[assembly:AutoDI.Settings(Behavior = AutoDI.Behaviors.SingleInterfaceImplementation | AutoDI.Behaviors.IncludeDependentAutoDIAssemblies)]
Having to declare every registration would be incredibly time consuming. To support many of the common scenarios AutoDI has several built in behaviors. Often it is simplest to let the behaviors do most of the mapping, and then fine tune the results with the MapAttribute
.
- None - All behaviors are turned off.
- Default - All behaviors are turned on.
-
SingleInterfaceImplementation - All classes that are the sole implementation of an interface are automatically registered with the interface as the service type. Mappings created by this behavior have a default lifetime of
LazySingleton
.
public interface IService { ... }
public class Service : IService { ... }
All dependencies of IService
will be automatically resolved with a Service
instance.
-
IncludeClasses - All classes are automatically registered with their own class type as the service type. This allows for dependencies to be requested by their concrete type. Abstract classes are ignored. Mappings created by this behavior have a default lifetime of
Transient
. -
IncludeBaseClasses - All classes are automatically registered with their base classes as the service types as long as they are the sole derived classes, and the class is not abstract. Mappings created by this behavior have a default lifetime of
Transient
. - IncludeDependentAutoDIAssemblies - By default only the main assembly is scanned when selecting types to register. Enabling this behavior will also scan any referenced assembly that references AutoDI. This can be useful for automatically picking up all of the assemblies that are also using AutoDI.
Default: InitMode.EntryPoint
[assembly:AutoDI.Settings(InitMode = InitMode.EntryPoint)]
Be default AutoDI will attempt to locate the assemblies entry point method (the main method) and inject a call to its initialize method there. You can disable this behavior by setting InitMode=InitMode.None
.
Disabling this behavior will require you to manually invoke AutoDI.DI.Init(...)
to initialize the AutoDI framework.
When producing libraries you expect to be consumed by others, it is recommended to use the module load initialization option (InitMode=InitMode.ModuleLoad
) so the consumer doesn't need to worry about manually initializing the AutoDI framework. AutoDI will detect the assembly's main module and inject the initialize method call in the module's static constructor.
For Xamarin.Android projects you will need to manually inject the container since the assemblies do not contain a single entry point.
Default: DebugLogLevel.Default
[assembly:AutoDI.Settings(DebugLogLevel = DebugLogLevel.Verbose)]
In the Build section of the Output window you can see the registrations that AutoDI is generating along with some basic output. If you are running into issues or simply want to see more details you can adjust the logging. The valid values are None, Default, or Verbose.
This setting does not affect any warnings or errors raised by AutoDI.
Default: False
[assembly:AutoDI.Settings(DebugExceptions = true)]
Allows for debugging thrown exceptions in the generated <AutoDI>.AddServices
method. The exceptions are caught, and wrapped into an AggregateException.
By default AutoDI will invoke the constructor with the most arguments. However, there may be cases where you want to explicitly specify which constructor to use. In these cases you can simply decorate the constructor with the AutoDI.DiConstructorAttribute
.
public class MyClass
{
public MyClass(string name, int id) { ... }
[AutoDI.DiConstructor]
public MyClass(IService service) { ...}
}
AutoDI can also inject dependencies into properties. For a property to be resolved it must meet the following criteria.
- Decorated with
[AutoDI.DependencyAttribute]
- Must have a setter or be an auto-property
The property values are set inside of the constructor (just like constructor parameters). This allows the properties to have any protection level, from public to private. Properties that only specify a getter method with a body are ignored.
For something things it may be necessary to configure the AutoDI framework at run-time.
To do this simply declare a method and decorate it with the AutoDI.SetupMethodAttribute
.
[AutoDI.SetupMethod]
public static void Initialize(AutoDI.IApplicationBuilder application)
{
//Any needed run-time configuration here
}
The setup method must meet the following criteria:
- Must be static
- Must be public or internal
- Must take in a single
AutoDI.IApplicationBuilder
parameter - Must be in the main module of the assembly being processed.
You can see examples of using the run-time configuration in some of the example projects.
You can register additional services using the IApplicationBuilder.ConfigureServices
method.
[AutoDI.SetupMethod]
public static void Initialize(AutoDI.IApplicationBuilder application)
{
application.ConfigureServices(services => services.AddAutoDILazySingleton<IService, Service>());
//you can also use the standard extension methods provided by the Microsoft.Extensions.DependencyInjection.Abstractions assembly, but this will limit you to object lifetimes supported there.
}
AutoDI's container is exposed through the AutoDI.IContainer
interface. Using the IApplicationBuilder.ConfigureContainer<AutoDI.IContainer>
method. You can use this method to make container specific calls.
[AutoDI.SetupMethod]
public static void Initialize(AutoDI.IApplicationBuilder application)
{
application.ConfigureContainer<AutoDI.IContainer>(container => {
//Manipulate the container
});
}
The IContainer
also has an event TypeKeyNotFound
. If a service cannot be resolved this event is raised. You can use this event as a run-time means of logging missing dependencies or dynamic resolution of missing types.
[AutoDI.SetupMethod]
public static void Initialize(AutoDI.IApplicationBuilder application)
{
application.ConfigureContainer<AutoDI.IContainer>(container => {
container.TypeKeyNotFound += (_, e) => {
e.Instance = ResolveInstance(e.ServiceKey);
};
});
}
private static object ResolveInstance(Type serviceKey)
{
//Return appropriate instance
}
If you are using a different dependency injection framework you can make a similar call to interact directly with that framework's container. The following is an example using StructureMap as the backing dependency injection framework.
[AutoDI.SetupMethod]
public static void Initialize(AutoDI.IApplicationBuilder application)
{
//This method provided by the StructureMap.AutoDI project in the Examples.
application.UseStructureMap();
application.ConfigureContinaer<Registry>(registry =>
{
Do any SM specific registration on the registry
});
}