Neadle
Neadle is a simple dependency injection container for .NET.
Usage
Just like any DI container, you register dependencies before you resolve them. Additionally you can test whether dependencies can be resolved at any time.
Registration
There are three ways to register a dependency:
// Instance: register a ready-made single instance to be returned whenever an Animal is requested
Di.Register<Animal>(new Dog("Fido"));
// Factory: register a factory instead of a ready-made instance
Di.Register<Animal>(() => new Cat("Felix"));
// Type: register a concrete type to be constructed on demand
Di.Register<Animal, Raven>();
For these last two methods you can also specify an InstanceScope, with one of the following values:
SingleInstance: Only one instance is created, and the same instance is returned for every subsequent requestSingleInstancePerLifetimeScope: Only one instance is created per lifetime scope, and the same instance is returned for every subsequent request within that scopeTransient: An instance is created for each request
Resolution
You can resolve a single dependency directly from the container:
// Who knows what type of animal lurks inside the container...
Animal beast = Di.Resolve<Animal>();
// No need to throw exceptions if no person was registered
if (Di.TryResolve<Person>(out Person someone))
{
...
}
Or create an instance of a class using constructor injection:
var instance = Di.CreateWithInjection<ClassWithParameterHeavyConstructor>();
If your class has multiple constructors, decorate one with the InjectAttribute:
public class MyClass
{
public MyClass()
{
}
[Inject]
public MyClass(ISomeDependency dependency)
{
}
}
Lifetime scopes
To prevent dependencies from "living" inside the container indefinitely, you can create a lifetime scope and resolve dependencies from it.
IDisposable dependencies are then disposed when the scope is disposed, however these dependencies must be registered:
- At the scope level, with
ownsInstance=true(ie:myScope.Register<Animal>(new Cow(), true)) - At the container level, with
InstanceScope.SingleInstancePerLifetimeScope
Di.Register<Dog, Dog>(InstanceScope.SingleInstance);
Di.Register<Cat, Cat>(InstanceScope.SingleInstancePerLifetimeScope);
using (var scope = Di.BeginLifetimeScope())
{
scope.Register<Horse, Horse>(new Horse(), false);
scope.Register<Cow, Cow>(new Cow(), true);
var animals = scope.Resolve(new Type[] { typeof(Dog), typeof(Cat), typeof(Cow) });
}
// If all types are IDisposable, Cat and Cow are now disposed.
Contexts
In addition to scopes, which act as a form of isolation, it is also possible to specify a context when registering or resolving dependencies. Dependencies from a matching context are favoured over the default (empty) context:
Di.Register("Hello ", "First");
Di.Register("World", "Second");
Di.Register("!");
// Writes "Hello World!"; where no match could be made for the "Third" context, it
// falls back to the context-less exclamation mark
Console.WriteLine(Di.Resolve<string>("First") + Di.Resolve<string>("Second") + Di.Resolve<string>("Third"));
Note that scopes and contexts are completely separate concepts, even though a scope can be created with a default context.