A Conventional .net dependency injection framework that will empower developers to write loosely coupled components.
- No default injection
- Lightweight, fast and zero boilerplate code required
- Registration and Auto-wiring through Attributes
- Inject dependencies by Type or Type name
- Lifetime manager to manage instance life, currently local and static
default
instances are being supported - Controlled scanning i.e Current Namespace
default
, Recursive Namespace and Assembly - Very little to learn to use this framework effectively
- Highly flexible and extensible design
Under Review - For review, please clone the repo and use the Mxfx.DI.Lab
project to get your hand dirty. Do share your feedback and suggestion for improvement and next step.
To Download the Msfx.DI nuget package, click here
Important This is a first preview release of the Msfx.DI library. If you encounter issues please log them in https://github.com/adansari/Msfx.DI/issues
Lets get started with very basic example.
[Injectable]
public abstract class Animal
{
public abstract void MakeSound();
}
An abstract class attributed as Injectable
- it is must to have this attribute to make type as injectable.
[Injectable]
public class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Meowwww");
}
}
Now, a class Cat
implementing the Animal
abstract class.
class Program
{
static void Main(string[] args)
{
DIContext di = new AttributeBasedDIContext(typeof(Program)).Scan();
Animal cat = di.Inject<Cat>();
cat.MakeSound();// Output => Meowwww
}
}
Finally, creating the DIContext
by passing the Type Program
to AttributeBasedDIContext
. By default the namespace where class Program
is located will be scanned for registering the dependencies attributed with [Injectable]
.
Here is a more elaborative usage.
[Injectable] // This attribute is a must for dependency injection
public abstract class Computer
{
protected OS _os;
protected Processor _processor;
public virtual Display Display { get; set; }
public virtual Processor Processor { get { return this._processor; } }
public virtual OS OS { get { return this._os; } }
public virtual RAM RAM { get; set; }
public abstract void Boot();
public abstract void InstallOS(OS osToInstall);
}
An abstract class Computer
composed of all its dependencies. And now here is the implementation.
[Injectable(InstanceType = InstanceType.Local)] // A non-static instance of Desktop will be injected
public class Desktop : Computer
{
protected Desktop() { } // A protected or private default constructor is needed
[AutoInject] // Constructor Injection
public Desktop([Inject(typeof(AMD))] Processor processor) // AMD Processor will be injected
{
this._processor = processor;
}
[AutoInject] // Property Injection
[Inject(typeof(CRTMonitor))] // CRTMonitor will be injected here
public override Display Display { get; set; }
[AutoInject] // Another Property Injection
[InjectValue(8)] // Value(s) i.e. 8 will be passed to RAM constructor
public override RAM RAM { get; set; }
[AutoInject] // Method Injection
public override void InstallOS(OS osToInstall) // Primary Target dependency will be injected for OS, refer the abstract class OS
{
this._os = osToInstall;
}
public override void Boot()
{
Console.WriteLine("System booting...");
Display.TurnON();
RAM.GetReady();
Processor.Compute();
OS.Operate();
}
}
And finally, let's have a one liner to instantiate a Desktop
with all its dependencies
var dIContext = new AttributeBasedDIContext(typeof(Program)).Scan();
var computer = dIContext.Inject<Desktop>();
computer.Boot();
Output of computer.Boot();
call
System booting...
CRTMonitor starting
8 GB - RAM getting ready
AMD Processor
Windows operating...
Couple of points to note:
-
Currently only Static and Non-Static i.e. local instance types are supported, by default static instance is created unless specified as
[Injectable(InstanceType = InstanceType.Local)]
-
You need to mark attribute
[AutoInject]
for Constructor, Method, Property and Field auto wiring. -
You can do the explicit injection for Constructor/Method Parameters, Property and Field by using the attribute
[Inject(typeof(..))]
i.e.[Inject(typeof(AMD))]
AMD will be injected for Processor -
If you don't specify the explicit injection the Primary target dependency will be injected. Please note, for abstract class and interface you must specify their Primary target dependency within their implementations if you are not using the explicit injection
-
You can use the attribute
[InjectValue(....)]
to supply the dependency's constructor parameters
Now let's look at its dependencies one by one. Here is the Processor
[Injectable]
public abstract class Processor { public abstract void Compute(); }
[Injectable]
[InjectFor(typeof(Processor))] // Setting the Intel as a Primary target dependency for Processor
public class Intel : Processor
{
public override void Compute() { Console.WriteLine("Intel Processor"); }
}
[Injectable]
public class AMD : Processor
{
public override void Compute() { Console.WriteLine("AMD Processor"); }
}
Then we have Displays
.
[Injectable]
public abstract class Display { public abstract void TurnON(); }
[Injectable]
public class CRTMonitor : Display
{
public override void TurnON() { Console.WriteLine("CRTMonitor starting"); }
}
[Injectable]
[InjectFor(typeof(Display))] // Setting the LCDMonitor as a Primary target dependency for Display
public class LCDMonitor : Display
{
public override void TurnON() { Console.WriteLine("LCD starting"); }
}
Then comes RAM
.
[Injectable]
public class RAM
{
protected RAM() { } // A protected or private default constructor is needed
protected int _sizeInGB;
public RAM(int sizeInGB) { this._sizeInGB = sizeInGB; }
public virtual void GetReady() { Console.WriteLine(_sizeInGB + " GB - RAM getting ready"); }
}
[Injectable]
public class DDRRAM : RAM
{
protected DDRRAM() { } // A protected or private default constructor is needed
public DDRRAM(int sizeInGB) : base(sizeInGB) { }
public override void GetReady() { Console.WriteLine(_sizeInGB + " GB - DDR RAM getting ready"); }
}
And finally an OS
.
[Injectable]
public abstract class OS { public abstract void Operate(); }
[Injectable]
[InjectFor(typeof(OS))] // Setting the Windows as a Primary target dependency for OS
public class Windows : OS
{
public override void Operate() { Console.WriteLine("Windows operating..."); }
}
[Injectable]
public class Linux : OS
{
public override void Operate() { Console.WriteLine("Linux operating..."); }
}
- Build on
netstandard2.0
and uses reflection - Uses ConcurrentDictionary<TKey,TValue> Class as underlying thread-safe data structure as dependency container
- Uses
MSTest
andMoq 4
for unit testing and mocking - Code coverage is ~100% with 85% unit tests and 15% integration tests
Coming soon..
Code of Conduct goes here
Coming soon..