Autofac.EasyPropInject
3.0.0
dotnet add package Autofac.EasyPropInject --version 3.0.0
NuGet\Install-Package Autofac.EasyPropInject -Version 3.0.0
<PackageReference Include="Autofac.EasyPropInject" Version="3.0.0" />
<PackageVersion Include="Autofac.EasyPropInject" Version="3.0.0" />
<PackageReference Include="Autofac.EasyPropInject" />
paket add Autofac.EasyPropInject --version 3.0.0
#r "nuget: Autofac.EasyPropInject, 3.0.0"
#:package Autofac.EasyPropInject@3.0.0
#addin nuget:?package=Autofac.EasyPropInject&version=3.0.0
#tool nuget:?package=Autofac.EasyPropInject&version=3.0.0
Autofac.EasyPropInject (by Malte)
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.
Requirements
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:
Install
Using the NuGet package manager:
Install-Package Autofac.EasyPropInject
Using the .NET CLI:
dotnet add package Autofac.EasyPropInject
Enable 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.
Configuration
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;
});
Configuration Entries and Their Meanings
| 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 |
Usage
The Attribute
To inject a property, decorate it with FromAutofacAttribute.
This is the simple way — no constructor plumbing, no service locator.
Arguments:
| 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. |
Examples
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.
Resolving as a Different Type
[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.
Unresolvable Properties
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.
Performance
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.
Changelog
See CHANGELOG.md.
Contribute / Donations
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.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 3.0.0 | 57 | 8/1/2026 | |
| 2.0.0 | 496 | 10/11/2024 | |
| 1.2.5 | 2,130 | 11/26/2021 | |
| 1.2.4 | 1,970 | 11/26/2021 | |
| 1.2.3 | 1,935 | 11/26/2021 | |
| 1.2.2 | 773 | 10/18/2020 | |
| 1.2.1 | 675 | 10/15/2020 | |
| 1.2.0 | 679 | 10/15/2020 | |
| 1.1.1 | 676 | 10/15/2020 | |
| 1.1.0 | 649 | 10/15/2020 | |
| 1.0.4 | 656 | 10/15/2020 | |
| 1.0.1 | 670 | 10/15/2020 | |
| 1.0.0 | 1,096 | 9/30/2018 |
* Upgraded to Autofac 9.x and added a net10.0 build next to netstandard2.1
* Roughly three times faster: property metadata is cached per type and written through emitted setters instead of using reflection on every resolve
* Fixed: private and protected properties declared on a base class were never injected
* Fixed: an unresolvable property was overwritten with null, wiping any value assigned in the constructor. It is now left untouched; the old behaviour is available via EasyPropInjectOptions.UnresolvedBehavior
* Added EasyPropInjectOptions, configurable via AddEasyPropInject(options => ...)
* AddEasyPropInject() can be called at any point while configuring the container - before, after or in between registrations