JSON/XML Configuration
Most IoC containers provide a programmatic interface as well as JSON/XML file-based configuration support, and Autofac is no exception.
Autofac encourages programmatic configuration through the ContainerBuilder
class. Using the programmatic interface is central to the design of the container. JSON or XML is recommended when concrete classes cannot be chosen or configured at compile-time.
Before diving too deeply into JSON/XML configuration, be sure to read Modules - this explains how to handle more complex scenarios than the basic JSON/XML component registration will allow. Configuration in JSON/XML is not a feature-for-feature replacement for programmatic configuration, so complex scenarios may require a combination of JSON/XML and modules.
Configuring With Microsoft Configuration (4.0+)
Note
Microsoft Configuration applies to the 4.0+ version of Autofac.Configuration. It does not work with previous versions of the configuration package.
With the release of Microsoft.Extensions.Configuration, and Autofac.Configuration 4.0.0, Autofac takes advantage of the more flexible configuration model not previously available when limited to application configuration files. If you were using the app.config
or web.config
based configuration available before, you will need to migrate your configuration to the new format and update the way you set configuration with your application container.
Quick Start
The basic steps to getting configuration set up with your application are:
Set up your configuration in JSON or XML files that can be read by
Microsoft.Extensions.Configuration
.JSON configuration uses
Microsoft.Extensions.Configuration.Json
XML configuration uses
Microsoft.Extensions.Configuration.Xml
Build the configuration using the
Microsoft.Extensions.Configuration.ConfigurationBuilder
.Create a new
Autofac.Configuration.ConfigurationModule
and pass the builtMicrosoft.Extensions.Configuration.IConfiguration
into it.Register the
Autofac.Configuration.ConfigurationModule
with your container.
A configuration file with some simple registrations looks like this:
{
"defaultAssembly": "Autofac.Example.Calculator",
"components": [{
"type": "Autofac.Example.Calculator.Addition.Add, Autofac.Example.Calculator.Addition",
"services": [{
"type": "Autofac.Example.Calculator.Api.IOperation"
}],
"injectProperties": true
}, {
"type": "Autofac.Example.Calculator.Division.Divide, Autofac.Example.Calculator.Division",
"services": [{
"type": "Autofac.Example.Calculator.Api.IOperation"
}],
"parameters": {
"places": 4
}
}]
}
JSON is cleaner and easier to read, but if you prefer XML, the same configuration looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<autofac defaultAssembly="Autofac.Example.Calculator">
<components name="0">
<type>Autofac.Example.Calculator.Addition.Add, Autofac.Example.Calculator.Addition</type>
<services name="0" type="Autofac.Example.Calculator.Api.IOperation" />
<injectProperties>true</injectProperties>
</components>
<components name="1">
<type>Autofac.Example.Calculator.Division.Divide, Autofac.Example.Calculator.Division</type>
<services name="0" type="Autofac.Example.Calculator.Api.IOperation" />
<injectProperties>true</injectProperties>
<parameters>
<places>4</places>
</parameters>
</components>
</autofac>
Note the ordinal “naming” of components and services in XML - this is due to the way Microsoft.Extensions.Configuration handles ordinal collections (arrays).
Build up your configuration and register it with the Autofac ContainerBuilder
like this:
// Add the configuration to the ConfigurationBuilder.
var config = new ConfigurationBuilder();
// config.AddJsonFile comes from Microsoft.Extensions.Configuration.Json
// config.AddXmlFile comes from Microsoft.Extensions.Configuration.Xml
config.AddJsonFile("autofac.json");
// Register the ConfigurationModule with Autofac.
var module = new ConfigurationModule(config.Build());
var builder = new ContainerBuilder();
builder.RegisterModule(module);
Default Assembly
You can specify a “default assembly” option in the configuration to help write types in a shorter fashion. If you don’t specify an assembly-qualified type name in a type or interface reference, it will be assumed to be in the default assembly.
{
"defaultAssembly": "Autofac.Example.Calculator"
}
Components
Components are the most common thing that you’ll register. You can specify several things on each component from lifetime scope to parameters.
Components are added to a top-level components
element in configuration. Inside that is an array of the components you want to register.
This example shows one component that has all of the options on it, just for syntax illustration purposes. You wouldn’t actually use every one of these in every component registration.
{
"components": [{
"type": "Autofac.Example.Calculator.Addition.Add, Autofac.Example.Calculator.Addition",
"services": [{
"type": "Autofac.Example.Calculator.Api.IOperation"
}, {
"type": "Autofac.Example.Calculator.Api.IAddOperation",
"key": "add"
}],
"autoActivate": true,
"injectProperties": true,
"instanceScope": "per-dependency",
"metadata": [{
"key": "answer",
"value": 42,
"type": "System.Int32, mscorlib"
}],
"ownership": "external",
"parameters": {
"places": 4
},
"properties": {
"DictionaryProp": {
"key": "value"
},
"ListProp": [1, 2, 3, 4, 5]
}
}]
}
Element Name |
Description |
Valid Values |
---|---|---|
|
The only required thing. The concrete class of the component (assembly-qualified if in an assembly other than the default). |
Any .NET type name that can be created through reflection. |
|
An array of services exposed by the component. Each service must have a |
Any .NET type name that can be created through reflection. |
|
A Boolean indicating if the component should auto-activate. |
|
|
A Boolean indicating whether property (setter) injection for the component should be enabled. |
|
|
Instance scope for the component. |
|
|
An array of metadata values to associate with the component. Each item specifies the |
Any metadata values. |
|
Allows you to control whether the lifetime scope disposes the component or your code does. |
|
|
A name/value dictionary where the name of each element is the name of a constructor parameter and the value is the value to inject. |
Any parameter in the constructor of the component type. |
|
A name/value dictionary where the name of each element is the name of a property and the value is the value to inject. |
Any settable property on the component type. |
Note that both parameters
and properties
support dictionary and enumerable values. You can see an example of how to specify those in the JSON structure, above.
Modules
When using modules with Autofac, you can register those modules along with components when using configuration.
Modules are added to a top-level modules
element in configuration. Inside that is an array of the modules you want to register.
This example shows one module that has all of the options on it, just for syntax illustration purposes. You wouldn’t actually use every one of these in every module registration.
{
"modules": [{
"type": "Autofac.Example.Calculator.OperationModule, Autofac.Example.Calculator",
"parameters": {
"places": 4
},
"properties": {
"DictionaryProp": {
"key": "value"
},
"ListProp": [1, 2, 3, 4, 5]
}
}]
}
Element Name |
Description |
Valid Values |
---|---|---|
|
The only required thing. The concrete class of the module (assembly-qualified if in an assembly other than the default). |
Any .NET type name that derives from |
|
A name/value dictionary where the name of each element is the name of a constructor parameter and the value is the value to inject. |
Any parameter in the constructor of the module type. |
|
A name/value dictionary where the name of each element is the name of a property and the value is the value to inject. |
Any settable property on the module type. |
Note that both parameters
and properties
support dictionary and enumerable values. You can see an example of how to specify those in the JSON structure, above.
You are allowed to register the same module multiple times using different parameter/property sets if you so choose.
Type Names
In all cases where you see a type name (component type, service types, module type) it is expected to be the standard, assembly qualified type name that you would normally be able to pass to Type.GetType(string typename)
. If the type is in the defaultAssembly
you can leave the assembly name off, but it doesn’t hurt to put it there regardless.
Assembly qualified type names have the full type with namespace, a comma, and the name of the assembly, like Autofac.Example.Calculator.OperationModule, Autofac.Example.Calculator
. In that case, Autofac.Example.Calculator.OperationModule
is the type and it’s in the Autofac.Example.Calculator
assembly.
Generics are a little more complicated. Configuration does not support open generics so you have to specify the fully qualified name of each of the generic parameters, too.
For example, say you have a repository IRepository<T>
in a ConfigWithGenericsDemo
assembly. Let’s also say you have a class StringRepository
that implements IRepository<string>
. To register that in configuration, it would look like this:
{
"components": [{
"type": "ConfigWithGenericsDemo.StringRepository, ConfigWithGenericsDemo",
"services": [{
"type": "ConfigWithGenericsDemo.IRepository`1[[System.String, mscorlib]], ConfigWithGenericsDemo"
}]
}]
}
If you’re having a difficult time figuring out what your type name is, you can always do something like this in code:
// Write the type name to the Debug output window and
// copy/paste it out of there into your config.
System.Diagnostics.Debug.WriteLine(typeof(IRepository<string>).AssemblyQualifiedName);
Differences from Legacy Configuration
When migrating from the legacy (pre 4.0 version) app.config
based format to the new format, there are some key changes to be aware of:
There is no ConfigurationSettingsReader.
Microsoft.Extensions.Configuration
has entirely replaced the old XML format configuration. The legacy configuration documentation does not apply to the 4.0+ series of configuration package.Multiple configuration files handled differently. The legacy configuration had a
files
element that would automatically pull several files together at once for configuration. Use theMicrosoft.Extensions.Configuration.ConfigurationBuilder
to accomplish this now.AutoActivate is supported. You can specify components should auto-activate now, a feature previously unavailable in configuration.
XML uses element children rather than attributes. This helps keep the XML and JSON parsing the same when using
Microsoft.Extensions.Configuration
so you can combine XML and JSON configuration sources correctly.Using XML requires you to name components and services with numbers.
Microsoft.Extensions.Configuration
requires every configuration item to have a name and a value. The way it supports ordinal collections (arrays) is that it implicitly gives unnamed elements in a collection names with numbers (“0”, “1”, and so on). You can see an example of this in the quick start, above. If you don’t go with JSON, you need to watch for this requirement fromMicrosoft.Extensions.Configuration
or you won’t get what you expect.Per-request lifetime scope is supported. Previously you couldn’t configure elements to have per-request lifetime scope. Now you can.
Dashes in names/values are gone. Names of XML elements used to include dashes like
inject-properties
- to work with the JSON configuration format, these are now camel-case, likeinjectProperties
.Services get specified in a child element. The legacy configuration allowed a service to be declared right at the top of the component. The new system requires all services be in the
services
collection.
Additional Tips
The new Microsoft.Extensions.Configuration
mechanism adds a lot of flexibility. Things you may want to take advantage of:
Environment variable support. You can use
Microsoft.Extensions.Configuration.EnvironmentVariables
to enable configuration changes based on the environment. A quick way to debug, patch, or fix something without touching code might be to switch an Autofac registration based on environment.Easy configuration merging. The
ConfigurationBuilder
allows you to create configuration from a lot of sources and merge them into one. If you have a lot of configuration, consider scanning for your configuration files and building the configuration dynamically rather than hard-coding paths.Custom configuration sources. You can implement
Microsoft.Extensions.Configuration.ConfigurationProvider
yourself backed by more than just files. If you want to centralize configuration, consider a database or REST API backed configuration source.
Configuring With Application Configuration (Legacy Pre-4.0)
Note
Legacy application configuration as described below applies to the 3.x and earlier versions of Autofac.Configuration. It does not work with the 4.0+ version of the package.
Prior to the release of Microsoft.Extensions.Configuration and the updated configuration model, Autofac tied into standard .NET application configuration files. (app.config
/ web.config
). In the 3.x series of the Autofac.Configuration package, this was the way to configure things.
Setup
Using the legacy configuration mechanism, you need to declare a section handler somewhere near the top of your config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration"/>
</configSections>
Then, provide a section describing your components:
<autofac defaultAssembly="Autofac.Example.Calculator.Api">
<components>
<component
type="Autofac.Example.Calculator.Addition.Add, Autofac.Example.Calculator.Addition"
service="Autofac.Example.Calculator.Api.IOperation" />
<component
type="Autofac.Example.Calculator.Division.Divide, Autofac.Example.Calculator.Division"
service="Autofac.Example.Calculator.Api.IOperation" >
<parameters>
<parameter name="places" value="4" />
</parameters>
</component>
The defaultAssembly
attribute is optional, allowing namespace-qualified rather than fully-qualified type names to be used. This can save some clutter and typing, especially if you use one configuration file per assembly (see Additional Config Files below.)
Components
Components are the most common thing that you’ll register. You can specify several things on each component from lifetime scope to parameters.
Component Attributes
The following can be used as attributes on the component
element (defaults are the same as for the programmatic API):
Attribute Name |
Description |
Valid Values |
---|---|---|
|
The only required attribute. The concrete class of the component (assembly-qualified if in an assembly other than the default.) |
Any .NET type name that can be created through reflection. |
|
A service exposed by the component. For more than one service, use the nested |
As for |
|
Instance scope - see Instance Scope. |
|
|
Container’s ownership over the instances - see the |
|
|
A string name for the component. |
Any non-empty string value. |
|
Enable property (setter) injection for the component. |
|
Component Child Elements
Element |
Description |
---|---|
|
A list of |
|
A list of explicit constructor parameters to set on the instances (see example above.) |
|
A list of explicit property values to set (syntax as for |
|
A list of |
There are some features missing from the XML configuration syntax that are available through the programmatic API - for example registration of generics. Using modules is recommended in these cases.
Modules
Configuring the container using components is very fine-grained and can get verbose quickly. Autofac has support for packaging components into Modules in order to encapsulate implementation while providing flexible configuration.
Modules are registered by type:
<modules>
<module type="MyModule" />
You can add nested parameters
and properties
to a module registration in the same manner as for components above.
Additional Config Files
You can include additional config files using:
<files>
<file name="Controllers.config" section="controllers" />
Configuring the Container
First, you must reference Autofac.Configuration.dll in from your project.
To configure the container use a ConfigurationSettingsReader
initialized with the name you gave to your XML configuration section:
var builder = new ContainerBuilder();
builder.RegisterModule(new ConfigurationSettingsReader("mycomponents"));
// Register other components and call Build() to create the container.
The container settings reader will override default components already registered; you can write your application so that it will run with sensible defaults and then override only those component registrations necessary for a particular deployment.
Multiple Files or Sections
You can use multiple settings readers in the same container, to read different sections or even different config files if the filename is supplied to the ConfigurationSettingsReader
constructor.