Skip to content

nmuhonen/Inferables-For-CLR

Repository files navigation

Inferables for CLR

Dependency Injection, arghh, a configuration eyesore! -even with exceptional lightweight frameworks like NInject and Castle Windsor. Hoping to change a small part of the world (or at least my world for that matter), I started working on a new solution on Github. Inferables For CLR is inspired by convention over configuration software approaches, providing a dependency resolution system that is based on where code is located and named relative to project namespaces, instead of a configuration mechanism. The result is easier management of standard injection patterns just by putting stuff in the right place :)

An Example:

Lets say your have some injectable classes defined somewhere- a set of loggers for instance:

namespace LoggerStuff
{
    public interface ILogger
    {
        void WriteMessage(string message);
    }

    // A trace logger
    public class TraceLogger: ILogger
    {
        public void WriteMessage(string message)
        {
            Console.Writeline(message);
        }
    }
    
    // A debug logger
    public class DebugLogger: ILogger
    {
        public void WriteMessage(string message)
        {
            Console.Writeline("DEBUG:" + message);
        }
    }
}

And lets say you want to inject them into a class:

using LoggerStuff;

namespace SnarkyServiceLib
{
    public interface ISnarkyService
    {
        string GetSnarkyMessage();   
    }
    
    public class SnarkyService: ISnarkyService
    {    
        private ILogger traceLogger;
        private ILogger debugLogger;  
    
        public SnarkyService(ILogger traceLogger, ILogger debugLogger)
        {
            this.traceLogger = traceLogger;
            this.debugLogger = debugLogger;
        }
        
        public string GetSnarkyMessage()
        {
            traceLogger.Write("Thinking...");
            traceLogger.Write("Still thinking...");
            traceLogger.Write("I've got it...");   
            debugLogger.Write("Not really thinking, just doing static code");
            
            string message = "SnarkyNess!";
        
            debugLogger.Write("Created Message.");
            
            return message;
        }
    }
}

With Inferables for clr, dependency injection is managed by relative namespace location, requiring no need for configuration in files or code. Thus, through the powers of the Inferables library:

using Inferables;
...

var module = ModuleManager.CreateModule("~");
var snarkyService = module.Get<ISnarkyService>();

var message = snarkyService.GetSnarkyMessage();

results in creating a SnarkyServiceLib.SnarkyService class, injecting LoggerStuff.TraceLogger and LoggerStuff.DebugLogger as inner members.

How it works:

With ModuleManager.CreateModule("~"), Inferables maps implementation classes to be found in the same namespace as their requested base definition. With such, it creates an implementation for

module.Get<ISnarkyService>()

that looks like this:

return new SnarkyService(new LoggerStuff.TraceLogger(), new LoggerStuff.DebugLogger());

Lets break down the steps of this wondrous magic:

  1. Inferables looks under the namespace SnarkyServiceLib to find an implementation for ISnarkyService- finding SnarkyService
  2. Next, it finds an appropriate constructor: public SnarkyService(ILogger traceLogger, ILogger debugLogger)
  3. Finally, it finds appropriate injectable matches to ILogger under its relative namespace LoggerStuff and based on the chosen names of the constructor parameters: TraceLogger and DebugLogger

How to override default injection patterns:

Lets say you want to inject something else besides the standard resolution pattern for testing, This can be done by adding another resolution pattern. For instance, lets you want to resolve a dependency to replace the DebugLogger. Simply put it under a new namespace resolution path:

namespace LoggerStuff.Mocks
{
    public class MockDebugLogger: ILogger
    {
        public void Write(string message)
        {
             Console.Writeline("Not really logging, just a mock :(");
        }  
    }
}

And change your code:

var module = ModuleManager.CreateModule("~.Mocks,~");
var snarkyService = module.Get<ISnarkyService>();

var message = snarkyService.GetSnarkyMessage();

With these changes, Inferables now acts like this:

  1. First, look for implementation types with locations starting with target type namespace with an additional .Mocks suffix.
  2. If it can't find it there, then look for implementation types that are found in the same namespace as the target type.

With these dependency resolution changes, Inferables creates an implementation for

module.Get<ISnarkyService>()

that looks like this:

return new SnarkyService(new LoggerStuff.TraceLogger(), new LoggerStuff.Mocks.MockDebugLogger());

Enjoy

Try it out for yourself: https://github.com/nmuhonen/Inferables-for-CLR and happy coding :)

About

Dependency Inference Framework for CLR

Resources

Stars

Watchers

Forks

Packages

No packages published