Skip to the content.

ZenAutofac

NuGet Version License: MIT

Brings the power of Zenject’s subcontainers syntax to Autofac, enabling Zenject-style dependency injection patterns in your Autofac applications. This library provides a familiar API for developers who love Zenject’s approach to scoping and subcontainers but want to leverage Autofac’s robust dependency injection capabilities.

🚀 Key Features

💡 Why Use ZenAutofac?

This library is ideal for:

📦 Installation

Install the package from NuGet:

Install-Package ZenAutofac

Or via the .NET CLI:

dotnet add package ZenAutofac

Quick Start

// Create your container builder
var builder = new ContainerBuilder();

// Register your service from subcontainer
builder
    .RegisterExtended<ISampleService>()
    .FromSubScope()
    .ByFunction(
        subcontainerBuilder =>
        {
            // Register service in the subcontainer
            // registered type should match type in RegisterFromSubScope
            subcontainerBuilder
                .RegisterType<SampleService>()
                .As<ISampleService>()
                .SingleInstance();

            // Register dependencies in the subcontainer
            // needed to run SampleService
            subcontainerBuilder
                .RegisterType<SampleDependency>()
                .SingleInstance();
        });

// ...

using var container = builder.Build();

container.Resolve<ISampleService>();

📚 Documentation

ℹ️ Info: All instances resolved from subcontainers have to implement IDisposer interface. This needed to limit subcontainers lifetime. When instance is disposed, it’s subcontainer will be disposed too. This is acheaved via IDisposer interface. More information here

Creating Subcontainers

Create isolated subcontainers for instance. Based on Autofac’s lifetime scope. Scope will inherit all dependencies from parent scope(s). In the same time scope could have own dependencies, that will be available only in this scope. See Quick Start for code sample. When FromSubScope() called it supposed to create new nested ILifetimeScope register required type and all it dependencies and resolve that type form created subcontainer. In other words target type have to be registered in subcontainer installer and implements IDisposer

Overall structure

Modules

Autofac’s modules could be used to register types in subcontainers. Module sample:

class SampleServiceModule : Module
{
    private readonly object _data;

    //pass all parameters to module's constructor as well as extra parameters or dependencies
    //from parent DI container(s)
    public SampleServiceModule(object data)
    {
        _data = data;
    }

    protected override void Load(ContainerBuilder builder)
    {
        //do ISampleService registration here
    }
}

Simple registrations:

builder
    .RegisterExtended<ISampleService>()
    .FromSubScope()
    .ByModule<SampleServiceModule>();

or even create module with using service provider. In that case module will be created in subcontainer and all dependencies from current scope will be available in the module.

builder
    .RegisterExtended<ISampleService>()
    .FromSubScope()
    .ByModule<SampleServiceModule>((scope) => scope.CreateInstance<SampleServiceModule>(parameter));

Decorators

Simple

Autofac default:

builder
    .RegisterDecorator<ServiceDecorator, IService>();

From Function

Use function to create decorator instance:

builder
    .RegisterDecoratorExtended<ServiceDecorator, IService>()
    .FromFunction(
        (context, baseService) =>
        {
            var parameters = new Parameters();

            return context.CreateInstance<ServiceDecorator>(baseService, parameters);
        });

With Subcontainer

ByFunction

Use subcontainer to create decorator:

builder
    .RegisterDecoratorExtended<ServiceDecorator, IService>()
    .FromSubScope()
    .ByFunction(
        (subcontainerBuilder, baseService) =>
        {
            // Register service in the subcontainer
            // registered type should match type in RegisterFromSubScope
            subcontainerBuilder
                .RegisterType<ServiceDecorator>()
                .WithParameters(TypedParameter.From(baseService))
                .SingleInstance();

            // Register dependencies in the subcontainer
            // needed to run ServiceDecorator
            subcontainerBuilder
                .RegisterType<SampleDependency>()
                .SingleInstance();
        });
By Module

As well instances could be created from module, decorator could also be created from module: In that case decoratable service will be passed to module constructor. Module sample:

class ServiceDecoratorModule : Module
{
    private readonly object _data;
    private readonly IService _service;

    //pass all parameters and decoratable service to module's constructor as well as extra parameters or dependencies
    //from parent DI container(s)
    public ServiceDecoratorModule(IService service, object data)
    {
        _data = data;
        _service = service;
    }

    protected override void Load(ContainerBuilder builder)
    {
        //do ServiceDecorator registration here
    }
}

registration sample:

builder
    .RegisterDecoratorExtended<ServiceDecorator, IService>()
    .FromSubScope()
    .ByModule<ServiceDecoratorModule>();

registration via service provider:

builder
    .RegisterDecoratorExtended<ServiceDecorator, IService>()
    .FromSubScope()
    .ByModule((scope, service) => scope.CreateInstance<ServiceDecoratorModule>(service, parameter));

Factories

IFactory

Provides interface IFactory<parameter1, parameter2, ... , parameterN, Iinstance> where parameter1, parameter2, ... , parameterN are parameters for factory creation and Iinstance is instance type.

From Functions

Use function to register IFactory<Iinstance> in DI:

builder
    .RegisterIFactoryExtended<Iinstance>()
    .FromFunction(
        scope => new Instance
        {
            Data = Guid.NewGuid()
        });

Use function to register IFactory<Guid, Iinstance> in DI:

builder
    .RegisterIFactoryExtended<Guid, Iinstance>()
    .FromFunction(
        (scope, parameter) => new Instance
        {
            Data = parameter
        });
From Subcontainers

Use subcontainer to create factory IFactory<Iinstance>, new subcontainer will be created when factory’s Create method is called:

By Function
builder
    .RegisterIFactoryExtended<Iinstance>()
    .FromSubScope()
    .ByFunction(subcontainerBuilder =>
    {
        // Register service in the subcontainer
        // registered type should match type in RegisterFactoryFromSubScope
        subcontainerBuilder
            .RegisterType<Iinstance>()
            .SingleInstance();

        // Register dependencies in the subcontainer
        // needed to run Iinstance
        subcontainerBuilder
            .RegisterType<SampleDependency>()
            .SingleInstance();
    });

Parameters edition:

builder
    .RegisterIFactoryExtended<Guid, Iinstance>()
    .FromSubScope()
    .ByFunction((subcontainerBuilder, parameter) =>
    {
        // Register service in the subcontainer
        // registered type should match type in RegisterFactoryFromSubScope
        subcontainerBuilder
            .RegisterType<Iinstance>()
            .WithParameters(TypedParameter.From(parameter))
            .SingleInstance();

        // Register dependencies in the subcontainer
        // needed to run Iinstance
        subcontainerBuilder
            .RegisterType<SampleDependency>()
            .SingleInstance();
    });
By Module

Registration factories to resolve from module. All parameters will be passed to module’s constructor. Module sample:

class ServiceModule : Module
{
    private readonly string _data;

    //pass all parameters to module's constructor as well as extra parameters or dependencies
    //from parent DI container(s)
    public ServiceModule(string data)
    {
        _data = data;
    }

    protected override void Load(ContainerBuilder builder)
    {
        //do IService registration here
    }
}

Registration sample:

builder
    .RegisterIFactoryExtended<string, IService>()
    .FromSubScope()
    .ByModule<ServiceModule>();

Registration sample with extra parameters via service provider:

builder
    .RegisterIFactoryExtended<string, IService>()
    .FromSubScope()
    .ByModule<ServiceModule>(
        (scope, arg3)
            => scope.CreateInstance<ServiceModule>(arg3, extraParameter));

Placeholders Factories

Used for registration of type distinct from interface IFactory, and/or modify factory `Create` behavior, like some instance initialization: Create new class and inherit from PlaceholderFactory:

class MyCustomFactory : PlaceholderFactory<Iinstance>
{
    public MyCustomFactory(SomeDependency dependency)
    {
        //...
    }

    //you can override Create method
    public override Iinstance Create()
    {
        return base.Create();
    }
}
From Functions

Placeholders Factories registration is available from function:

builder
    .RegisterPlaceholderFactoryExtended<Iinstance, MyCustomFactory>()
    .FromFunction(
        scope => new Instance
        {
            Data = Guid.NewGuid()
        });
From Subcontainers

and for subcontainers, new subcontainer will be created and installer called to register all content in subcontainer

By Function

via function

builder
    .RegisterPlaceholderFactoryExtended<
        Guid,
        Iinstance,
        MyCustomFactory>()
    .FromSubScope()
    .ByFunction((subcontainerBuilder, parameter) =>
    {
        // Register service in the subcontainer
        // registered type should match type in RegisterPlaceholderFactoryExtended
        subcontainerBuilder
            .RegisterType<Iinstance>()
            .WithParameters(TypedParameter.From(parameter))
            .SingleInstance();

        // Register dependencies in the subcontainer
        // needed to run Iinstance
        subcontainerBuilder
            .RegisterType<SampleDependency>()
            .SingleInstance();
    });
By Module

For module sample see by module section in IFactory registration. The registration is similar to IFactory registration. And could reuse same modules.

Registration sample:

builder
    .RegisterPlaceholderFactoryExtended<string, IService, MyCustomServiceFactory>()
    .FromSubScope()
    .ByModule<ServiceModule>();

Registration sample with extra parameters via CreateInstance:

builder
    .RegisterPlaceholderFactoryExtended<string, IService, MyCustomServiceFactory>()
    .FromSubScope()
    .ByModule<ServiceModule>(
        (scope, arg3)
            => scope.CreateInstance<ServiceModule>(arg3, extraParameter));

where placeholder factory type is

class MyCustomServiceFactory : PlaceholderFactory<string, IService>
{
    public MyCustomServiceFactory(SomeDependency dependency)
    {
        //...
    }

    //you can override Create method
    public override IService Create(string arg)
    {
        return base.Create(arg);
    }
}

Other Helpers

CompositeDisposable

CompositeDisposable is removed from the project, please use Disposer instead, see this

CreateInstance

IComponentContext.CreateInstance

Use IComponentContext.CreateInstance to create instance of type with using DI container. Internally transforms IComponentContext to IServiceProvider and create instance via reflection. In most cases works, but for some advanced features please use ILifetimeScope.CreateInstance example:

builder
    .Register<SampleService>(context =>
    {
        var result = context.CreateInstance<SampleService>(Guid.NewGuid());

        result.Setup();

        return result;
    })
    .As<ISampleService>()
    .SingleInstance();
ILifetimeScope.CreateInstance

Use ILifetimeScope.CreateInstance to create instance of type with using DI container. Internally creates new lifetime scope and creates T there. Use it for most accurate instance creation. In spite of usage Autofac API this approach should support autofac features like Autofac attributes etc. example:

builder
    .Register<SampleService>(context =>
    {
        var result = context.CreateInstance<SampleService>(TypedParameter.From(Guid.NewGuid()));

        result.Setup();

        return result;
    })
    .As<ISampleService>()
    .SingleInstance();

RegisterModuleWithArgs

Used to register module in autofac style, don’t pass any dependencies from DI. Only one provided in parameters.

var builder = new ContainerBuilder();

builder.RegisterModuleWithArgs<TestModule>(
                new NamedParameter("stringValue", stringParameter),
                new NamedParameter("intValue", intParameter));

RegisterModuleWithArgs

Used to register module in autofac style, don’t pass any dependencies from DI. Only one provided in parameters.

var builder = new ContainerBuilder();

builder.RegisterModuleWithArgs<TestModule>(
                new NamedParameter("stringValue", stringParameter),
                new NamedParameter("intValue", intParameter));

Harmony is no longer used in ZenAutofac due to unstable behavior when writing unit tests and incompatibility with environments like IL2CPP that do not support runtime patching.

As mentioned earlier, every instance resolved from a subcontainer must implement the IDisposer interface. The subcontainer will be disposed when that instance is disposed.

IDisposer is required to attach a subcontainer to an instance using the IDisposer.AddInstanceForDisposal method.

To implement IDisposer, you can use composition: implement IDisposer on your entity and forward all IDisposer method calls to ZenAutofac.Entities.Disposer.

Be aware that registered instances will be disposed in the same order in which they were added.

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments