Skip to content

Configuration Legacy

Kevin Bost edited this page Dec 21, 2018 · 1 revision

This information applies ot AudiDI 3.x and is kept for historical reference

For the latest infomration see here

Compile time configuration (FodyWeavers.xml)

Be sure to do a full rebuild of your project after modifying FodyWeavers.xml

Run time configuration ([SetupMethodAttribute])

Compile time configuration

The following configurations are all specified within the AutoDI node in the FodyWeavers.xml file. All of the node names and attributes are case insensitive. These configurations are processed in order, so if there are rules that match the same type, the last one wins.

Generate Debuggable Code (v3.3.0+)

Default: None

<AutoDI DebugCodeGeneration="CSharp"/>

Valid values: None, CSharp

[assembly:AutoDI.Settings(DebugCodeGeneration = 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 in the final output.

Generate Registrations

Default: True

<AutoDI GenerateRegistrations="false" />
[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.

Included assemblies

Default: none

<AutoDI>
  <Assembly Name="MyCompany.*" />
</AutoDI>

AutoDI will always scan the assembly being processed by the Fody weaver (ie. the assembly with the AutoDI.Fody 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 <assembly /> node inside of the AutoDI node. The Name attribute is a regular expression that is used to match on the assembly's full name.

Custom mapping

Default: none

<AutoDI>
  <Map From=".I*" To=".*" Lifetime="Singleton" />
  <Map From="regex:Interfaces\.I(.*)" To="regex:Services\.$1" Force="False" />
</AutoDI>

If you need to configure a specific mapping between types you can specify it with a <map /> node specifying the From and To types. Both of these attributes may contain a wildcard (*). In the first example <Map From=".I*" To=".*" /> 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 To attribute will be replaced with the matched wildcard value in the From attribute.

The From and To attributes 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 To attribute may contain regex groups that were matched in the From attribute. This allows for some more complex mappings to be declared.

Map nodes may also specify a Lifetime attribute for all of the mappings that are generated from it. This lifetime may be overridden by Type nodes (see below for valid values). Mapping that do not have an explicit mapping will use LazySingleton.

AutoDI will also verify that the target type (specified in the To attribute) can be cast to the service type (specified in the From 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 attribute to true (default is false).

When matching on nested types you may use either '/' or '+' to denote a nested class. <Map From="Container+*" To="Container/*" />

Note: In v3.0.0 all From and To attribute values were treated as regex (without the "regex:" prefix)

Object lifetime

Default: none

<AutoDI>
  <Type Name="Namespace\..+Service" Lifetime="Transient" />
</AutoDI>

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 a <type /> node. The Name attribute is processed as a regular expression. 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.

<AutoDI>
  <type name="Exclude\..*" lifetime="None" />
</AutoDI>

Behaviors

Default: Behaviors.Default

<AutoDI Behavior="SingleInterfaceImplementation,IncludeClasses,IncludeBaseClasses,IncludeDependentAutoDIAssemblies" />
[assembly:AutoDI.Settings(Behavior = Behaviors.SingleInterfaceImplementation | Behaviors.IncludeClasses)]

Having to declare every registration with regex 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 <map /> or <type /> nodes. You can specify any number of behaviors as a comma separated list.

Behavior list

  • 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.

Auto Initialization

Default: True

<AutoDI AutoInit="False" />
[assembly:AutoDI.Settings(AutoInit = false)]

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 AutoInit="False". Disabling this behavior will require you to manually invoke AutoDI.DI.Init(...) to initialize the AutoDI framework.

For Xamarin.Android projects you will need to manually inject the container since the assemblies do not contain a single entry point.

Debug Log Level

Default: DebugLogLevel.Default

<AutoDI DebugLogLevel="Verbose" />
[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.

Debug Exceptions

Default: False

<AutoDI DebugExceptions="True" />

Introduced in version 3.2.0. Allows for debugging thrown exceptions in the generated <AutoDI>.AddServices. The exceptions are caught, and wrapped into an AggregateException.

Constructor selection

By default AutoDI will invoke the constructor with the most arguments. However, there may be cases where you need 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) { ...}
}

Property Injection

AutoDI can also provide property injection for dependencies. 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.

Modifying the container at run-time

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 by the Fody weaver.

You can see examples of using the run-time configuration in some of the example projects.

Registering services

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.
}

Container configuration

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
    });
}