Attribute based property injection for Autofac, without any other helper libraries.
Decorate a property with [FromAutofac] and it is resolved from the container while the component is activated — public, protected and private properties all work.
Property metadata is collected once per type and written through emitted setters, so after the first resolve the injection costs close to nothing.
Autofac.EasyPropInject requires Autofac 9.x and a project targeting net10.0 or anything compatible with netstandard2.1.
External Libraries
This library uses the following library to achieve its functionality:
Using the NuGet package manager:
Install-Package Autofac.EasyPropInjectUsing the .NET CLI:
dotnet add package Autofac.EasyPropInjectEnable it in your application:
// Startup.cs / Program.cs
using Autofac;
using Autofac.EasyPropInject;
var builder = new ContainerBuilder();
builder.AddEasyPropInject();
// Add your services as usual
builder.RegisterType<MyService>().As<IMyService>();
IContainer container = builder.Build();Note: the call can go anywhere while configuring the builder — before, after or in between your registrations. Older versions of this README claimed it had to come first; that is no longer the case and it is covered by tests.
Set up the Autofac container the way your framework expects — see the Autofac integration documentation.
No configuration is required to use Autofac.EasyPropInject. If you want to change the defaults, pass a
callback to AddEasyPropInject:
builder.AddEasyPropInject(options =>
{
// Restore the behaviour of 2.x: overwrite a property with null when it cannot be resolved
options.UnresolvedBehavior = UnresolvedPropertyBehavior.SetNull;
});| Option | Description | Default Value | Type |
|---|---|---|---|
| UnresolvedBehavior | What happens to a decorated property whose service is not registered in the container. Skip leaves the property untouched, so a value assigned in the constructor survives. SetNull overwrites it with null, which is what EasyPropInject 2.x and earlier did |
Skip | UnresolvedPropertyBehavior |
To inject a property, decorate it with FromAutofacAttribute.
This is the simple way — no constructor plumbing, no service locator.
| Name | Description |
|---|---|
| Type resolveFromRegisteredType | Look this type up in the container instead of the declared property type. If null, empty, or not given, the declared property type is used. |
using Autofac.EasyPropInject.Annotations;
public class MyClass : IMyInterface
{
public MyClass(ISomeOtherInterface someService)
{
}
[FromAutofac]
public IUnitOfWork Unit { get; set; }
[FromAutofac] // Since 1.2.5: support for protected properties
protected IMyService ServiceProtected { get; set; }
[FromAutofac] // Since 1.2.5: support for private properties
private IMyService2 ServicePrivate { get; set; }
[FromAutofac(typeof(IUnitOfWork))]
public UnitOfWorkBaseClass Unit2 { get; set; }
}Once IMyInterface is resolved and activated, EasyPropInject resolves every decorated property from the
current container. Since 3.0.0, private and protected properties declared on a base class are picked up
as well.
The property must have a setter — init accessors are fine. A decorated property without one throws an
InvalidOperationException naming the type and the property.
[FromAutofac(typeof(IUnitOfWork))] resolves IUnitOfWork from the container and assigns it to a property
declared as UnitOfWorkBaseClass.
Warning: this feature is handy but truly unsafe. No compile time check and no type check at resolve time can tell you that the registered implementation of
IUnitOfWorkreally is aUnitOfWorkBaseClass— be sure of what you are doing.
By default a property whose service is not registered is left exactly as it is:
public class MyClass : IMyInterface
{
public MyClass()
{
this.Optional = new NullLogger();
}
[FromAutofac]
public ILogger Optional { get; set; } // stays a NullLogger if ILogger is not registered
}Set UnresolvedBehavior to UnresolvedPropertyBehavior.SetNull to get the 2.x behaviour back, where the
property is overwritten with null instead.
EasyPropInject sits in the resolve pipeline of every component, so it is built to get out of the way. Version 3.0.0 resolves roughly three times faster than 2.x — measured over 2,000,000 resolves on .NET 10:
| Scenario | 2.x | 3.0.0 |
|---|---|---|
Component with [FromAutofac] properties |
~3.2 s | ~1.0 s |
| Component without any attributes | ~2.0 s | ~0.7 s |
What changed:
- Metadata is cached per type. The decorated properties, the Autofac
Serviceto resolve for each of them and the setter to use are worked out once and reused. Up to 2.x every resolve calledGetProperties()on the concrete type and asked every single property for its attributes. - Types without decorated properties cost a dictionary lookup. They cache an empty result and leave the middleware immediately — which is why components that never use the attribute got faster too.
- Setters are emitted, not reflected. Each property gets a
DynamicMethodthat assigns the value directly, which avoids the overhead and the boxing ofPropertyInfo.SetValue. Where dynamic code is not available (AOT), it falls back to reflection automatically. - Shared instances are injected once, when they are activated, and skipped explicitly on every resolve after that.
- One middleware instance per container instead of one per component registration.
See CHANGELOG.md.
If you have any ideas to improve my projects, feel free to send a pull request.
If you like my work and want to support me (or want to buy me a coffee/beer), PayPal donations are more than appreciated.