ZenAutofac
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
- Zenject-inspired subcontainer syntax for Autofac
- Subcontainer lifetime management
- Seamless integration of Zenject’s scoping patterns
- Simplified decorator registration with isolated scopes
- Full compatibility with existing Autofac applications
IFactory<...>andPlaceholderFactory<...>for factory registration with subcontainer support
💡 Why Use ZenAutofac?
This library is ideal for:
- Applications requiring advanced dependency injection scenarios with clean scoping
- Developers who prefer to split business logic and infrastructure
📦 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
IDisposerinterface. This needed to limit subcontainers lifetime. When instance is disposed, it’s subcontainer will be disposed too. This is acheaved viaIDisposerinterface. 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
-
Type registration:
- from subScope:
-
- IFactory<P0, P1, … , PN, TInstance>
- from new instance
- from function
- from subScope:
- PlaceholderFactory<P0, P1, … , PN, TInstance>
- from new instance
- from function
- from subScope:
- IFactory<P0, P1, … , PN, TInstance>
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
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));
HarmonyPatch Related
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.
Disposer Related
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.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- Inspired by Zenject’s clean subcontainer API
- Built on top of Autofac’s powerful DI container
- Thanks to all contributors who helped improve this project