Butler is an easy-to-use and beginner-friendly IoC (Inversion of Control) container designed for performance, low-memory consumption and easy integrability.
- 🔌 At-Runtime Service Registration
- ✳️ Dependency Resolving
- 📝 Resolve Tracing
- ⚡ Advanced Debugging
- ⏱️ Service Lifetimes
- ⬆ Lightweight
and a lot more...
- Download Butler.IoC
- Download Butler.IoC.Development
- .NET Core 1.0, .NET Framework 2.0*, .NET Standard 1.0 or Mono 4.6
Note: .NET Framework 2.0 does not support full reflection, since .NET Framework 3.5 all features of Butler are supported.
Let's get started with an easy example of resolving a basic service with a single dependency:
// create a new root container
using (var container = new RootContainer())
{
// register services (1)
container.Register<ISomeDependency, SomeDependency>();
container.Register<ISomeService, SomeService>();
// resolve the service (2)
var myService = container.Resolve<ISomeService>();
// do something with the service
myService.DoSomething();
}
In the above example the service and a dependency is registered (1). Then the service is resolved. (2)
You can register as many services as you want, there are several methods to register a service.
The container keeps care of dependency resolution and disposation of the service and dependencies. You can control the service lifetime using
service lifetimes. Butler comes with three service-lifetimes out of the box: Singleton
, Transient
and Scoped
. By default Butler uses the
Transient lifetime which recreates the service each request / resolve.
The singleton service lifetime (Lifetime.Singleton
)
creates the service once, the instance is shared for all creations.
The scoped service lifetime (Lifetime.Scoped
) creates the service for each new scope. You can set the scope key in the
resolve method when resolving the service. All resolves will retrieve the same service instance with the same scope key.
The transient service lifetime (Lifetime.Transient
) creates a new service each
resolve. This should be used for state-less small services. (Note: By default Butler does not track / dispose
transient services, this can be enabled using the TrackDisposableTransients
property)
You can implement lifetimes by implementing the ILifetime interface. Note that your ILifetime should be a singleton object.
The default registration is used for services that may have dependencies. This registration can have all types of lifetimes.
// register ISomeService with SomeService as implementation as transient (default)
myContainer.Register<ISomeService, SomeService>();
// register SomeService as a singleton service
myContainer.Register<SomeService>().AsSingleton();
// register ISomeService with SomeService as implementation with a custom lifetime
myContainer.Register<ISomeService, SomeService>().WithLifetime(MyCustomLifetime);
A direct registration is a registration for a service that has no dependencies and is constructed over a parameter-less constructor.
// register direct service (Note: SomeService must have a parameter-less constructor!)
myContainer.RegisterDirect<ISomeService, SomeService>();
A factory registration is a registration that is retrieved from a factory (see: ServiceFactory<T>
).
// register a DateTimeOffset factory which returns the current UTC time offset
myContainer.RegisterFactory<DateTimeOffset>(context => DateTimeOffset.UtcNow);
var currentTimeUtc = myContainer.Resolve<DateTimeOffset>();
Console.WriteLine($"Current Time: {currentTimeUtc} UTC.");
// > Current Time: 8/18/2019 12:36:43 PM +00:00 UTC.
An instance registration is a registration for already supplied services. The registration must keep care of disposing the instance itself.
// create the service instance
var myService = new SomeService();
// register the service instance
myContainer.RegisterInstance<ISomeService, SomeService>(myService);
// do something with the service
myContainer.Resolve<ISomeService>().DoSomething();
// dispose the instance
myService.Dispose();
A multi-registration can contain as the name says multiple registrations in once: For example you want to register multiple services with the same service type:
// enable automatic multi-registration creation
myContainer.DefaultRegistrationMode = ServiceRegistrationMode.Append;
// register multiple registrations with the same type
myContainer.Register<ISomeService, SomeService>();
myContainer.Register<ISomeService, SomeOtherService>();
// resolve all
myContainer.ResolveAll<ISomeService>();
// --> Contains an instance of SomeService and SomeOtherService
Note: The service instances are created when enumerating through the enumerable.