factory.md

July 3, 2026 ยท View on GitHub

Factory

Constructor injection covers most cases, but sometimes an instance needs extra work before it is ready to use โ€” like the Connect() call here that opens a database connection. A factory binding To<T>(ctx => ...) puts that creation logic under your control: call ctx.Inject(out var dependency) to have the container provide dependencies, run any setup code, then return the finished instance.

using Shouldly;
using Pure.DI;

DI.Setup(nameof(Composition))
    .Bind<IDatabaseService>().To<DatabaseService>(ctx => {
        // Some logic for creating an instance.
        // For example, we need to manually initialize the connection.
        ctx.Inject(out DatabaseService service);
        service.Connect();
        return service;
    })
    .Bind<IUserRegistry>().To<UserRegistry>()

    // Composition root
    .Root<IUserRegistry>("Registry");

var composition = new Composition();
var registry = composition.Registry;
registry.Database.IsConnected.ShouldBeTrue();

interface IDatabaseService
{
    bool IsConnected { get; }
}

class DatabaseService : IDatabaseService
{
    public bool IsConnected { get; private set; }

    // Simulates a connection establishment that must be called explicitly
    public void Connect() => IsConnected = true;
}

interface IUserRegistry
{
    IDatabaseService Database { get; }
}

class UserRegistry(IDatabaseService database) : IUserRegistry
{
    public IDatabaseService Database { get; } = database;
}
Running this code sample locally
dotnet --list-sdk
  • Create a net10.0 (or later) console application
dotnet new console -n Sample
dotnet add package Pure.DI
dotnet add package Shouldly
  • Copy the example code into the Program.cs file

You are ready to run the example ๐Ÿš€

dotnet run

There are scenarios where manual control over the creation process is required, such as

  • When additional initialization logic is needed
  • When complex construction steps are required
  • When specific object states need to be set during creation

Important

The method Inject() cannot be used outside of the binding setup. Limitations: factory bindings introduce custom construction logic that must be maintained and tested. Common pitfalls:

The following partial class will be generated
partial class Composition
{
  public IUserRegistry Registry
  {
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    get
    {
      DatabaseService transientDatabaseService;
      // Some logic for creating an instance.
      // For example, we need to manually initialize the connection.
      DatabaseService localService = new DatabaseService();
      localService.Connect();
      transientDatabaseService = localService;
      return new UserRegistry(transientDatabaseService);
    }
  }
}

Class diagram:

---
 config:
  class:
   hideEmptyMembersBox: true
---
classDiagram
	DatabaseService --|> IDatabaseService
	UserRegistry --|> IUserRegistry
	Composition ..> UserRegistry : IUserRegistry Registry
	UserRegistry *-- DatabaseService : IDatabaseService
	namespace Pure.DI.UsageTests.Basics.FactoryScenario {
		class Composition {
		<<partial>>
		+IUserRegistry Registry
		}
		class DatabaseService {
				<<class>>
		}
		class IDatabaseService {
			<<interface>>
		}
		class IUserRegistry {
			<<interface>>
		}
		class UserRegistry {
				<<class>>
			+UserRegistry(IDatabaseService database)
		}
	}

See also: