Autofac.EasyPropInject 3.0.0

dotnet add package Autofac.EasyPropInject --version 3.0.0
                    
NuGet\Install-Package Autofac.EasyPropInject -Version 3.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Autofac.EasyPropInject" Version="3.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Autofac.EasyPropInject" Version="3.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Autofac.EasyPropInject" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Autofac.EasyPropInject --version 3.0.0
                    
#r "nuget: Autofac.EasyPropInject, 3.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Autofac.EasyPropInject@3.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Autofac.EasyPropInject&version=3.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Autofac.EasyPropInject&version=3.0.0
                    
Install as a Cake Tool

Nuget Downloads Paypal Donate Pull Request Check

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 IUnitOfWork really is a UnitOfWorkBaseClass — 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 Service to resolve for each of them and the setter to use are worked out once and reused. Up to 2.x every resolve called GetProperties() 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 DynamicMethod that assigns the value directly, which avoids the overhead and the boxing of PropertyInfo.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.

Paypal Donate Nuget

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

* 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