Dependency Injection
and Dependency Injection Containers

Created by Misha Bergal and Valerij Petrulevich

What do we want?

Build high-value maintainable systems on time

Please Mind the Gap

No silver bullet, but simplicity is the key

We need to keep the code as simple as possible, so it's easier to understand and modify. Developers spend far more time reading code than writing it, so that's what we should optimize for it. -- Steeve Freeman, Nat Pryce, Growing Object-Oriented Software, Guided by Tests

Simplicity through composition

Compose systems of highly cohesive, low coupled components/parts.

    • Cohesive = focused
    • Low coupled = needs low number of other components to do its work

  • Both make it easier to understand, reason about component behavior
  • Many components reusable between systems. Tested, well known, etc.
  • Components create an easily recognizable structure in software (code)

Composition paradigms

  • Object Composition

Bash


        ps -A | grep httpd
        ls | grep UofI | wc -l

Powershell


        Get-Children  . | Select-Object Name | Where-Object {$_.StartsWith( "A" )}

OOP inheritance, mixins (cake), Ruby modules

Functional composition

Object composition example

System Design

  1. Application: receives request and routes to controller/action
  2. FeedbackController/Index
    1. Creates MailMessage from input
    2. Sends generated MailMessage
      1. Sends it to SMTP Server specified in settings
      2. Records sent message in the database

Class Diagram

Typical implementation - do not try at home


        class EmailService
            {
            public void SendEmail( MailMessage mail )
                {
                // Looking up email service settings setting 8-/
                var settings = EmailServiceSettings.LoadFromConfig();

                new TransactionalSMTPClient( settings.Host ).Send( mail );

                using ( var dataContext = new DataContext() )
                // or better
                using ( var dataContext = DataContextFactory.Instance.CreateDataContext() )
                    {
                    dataContext.AddObject( mail );
                    dataContext.SaveChanges();
                    }
                }
            }

Problem 1: Implicit dependencies

  • What do we need to use it in the system?

        public class EmailService
            {
            public void SendEmail( MailMessage mail )
                {
                ... a lot of code ...
                ... a lot of code ...
                /* Somewhere here reads settings */
                ... a lot of code ...
                /* Somewhere here saves in the database */
                ... a lot of code ...
                ... a lot of code ...
                ... a lot of code ...
                }
            }
        

There is a substantial gap between design and code (code is not expressive)

Problem 2: Lazy loading of settings


            public void SendEmail( MailMessage mail )
                {
                var settings = EmailServiceSettings.LoadFromConfig();

        

Problem 3: Instantiating concrete types and not using a factory


            using ( var dataContext = new DataContext() )

        

Implementation with explicit dependencies, Dependency Injection


    public class EmailService
        {
        public EmailService(
                     EmailServiceSettings emailServiceSettings, // <-- dependency
                     IEmailLogger         emailLogger ) // <-- dependency
            {
            _emailServiceSettings = emailServiceSettings;
            _emailLogger = emailLogger;
            }

        public void SendMail( MailMessage mail )
            {
            new SmtpClient( _emailServiceSettings.Host ).Send( mail );
            _emailLogger.Record( mail );
            }

        private readonly EmailServiceSettings _emailServiceSettings;
        private readonly IEmailLogger         _emailLogger;
        }

Implementation with explicit dependencies, Dependency Injection


    public class EmailService
        {
        public EmailService( EmailServiceSettings emailServiceSettings, // <-- dependency
                             IEmailLogger         emailLogger ) // <-- dependency
            {
            ...

  1. Sends it to SMTP Server specified in settings
  2. Records sent message in the database

Inverse of dependency injection - Service Locator

A boobytrap!


    class EmailService
        {
        public void SendEmail( MailMessage mail )
            {
            var settings = EmailServiceSettings.LoadFromConfig();
            using ( var dataContext = DataContextFactory.Instance.CreateDataContext() )
                {
                ...
                }
            }
        }

Will see later when it is useful and ok to use

Rules for expressing dependencies in code

Design Term Representation in Code
Required dependency Constructor
Optional dependency Property
Operation dependency Method parameter

Required Dependency


public class EmailService
    {
    public EmailService( EmailServiceSettings emailServiceSettings, // <-- required dependency
                         IEmailLogger         emailLogger ) // <-- required dependency
        {
        ...

Optional Dependency


public class EmailService
    {
    public EmailService( EmailServiceSettings emailServiceSettings, // <-- required dependency
        {
        EmailLogger = new NullLogger();
        }      
    
    public IEmailLogger         EmailLogger { get; set; } // <-- optional dependency
    ...
    }

Method Dependency


public class Order
    {
    public void CalculateTaxes( TaxCalculator taxCalculator )
        {
        ....
        }
    }

You can do DI manually and some prefer it that way


         Post["/Feedback"] = parameters =>
            {
            var dataContextFactory = DbContextFactory<DataContext>.BuildFactory( "DemoApp" );

            var emailServiceSettings = _settingsManager.Load<EmailServiceSettings>();

            using ( var dataContext = dataContextFactory.GetDataContext() )
                {
                var emailService = new EmailService( emailServiceSettings, dataContext );
                var controller = new FeedbackController( new EmailGenerator(), emailService );

                return controller.Index( ... );
                } // Close dataContext
            };
        

But as system becomes larger, setting up (and managing) dependencies becomes more painful


Post["/Feedback"] = parameters =>
    {
    var dataContextFactory = DbContextFactory<DataContext>.BuildFactory( "DemoApp" );
    var emailServiceSettings = _settingsManager.Load<EmailServiceSettings>();
    using ( var dataContext = dataContextFactory.GetDataContext() ) {
        var emailService = new EmailService( emailServiceSettings, dataContext );
        var controller = new FeedbackController( new EmailGenerator(), emailService );
        ...
        }
    };
Post["/SomethingElse"] = parameters =>
    {
    var dataContextFactory = DbContextFactory<DataContext>.BuildFactory( "DemoApp" );
    var emailServiceSettings = _settingsManager.Load<EmailServiceSettings>();
    using ( var dataContext = dataContextFactory.GetDataContext() ) {
        var emailService = new EmailService( emailServiceSettings, dataContext );
        var controller = new FeedbackController( new EmailGenerator(), emailService );
        ...

We can try to reuse some component setup functionality


    void SetUpComponents()
        {
        var dataContextFactory = DbContextFactory<DataContext>.BuildFactory( "DemoApp" );

        _dataContext = dataContextFactory.GetDataContext();
        _emailService = new EmailService( emailServiceSettings, _dataContext );
        }


    Post["/Feedback"] = parameters =>
        {
        SetUpComponents();
        var controller =  new FeedbackController( new EmailGenerator(), _emailService );
        ...

But now we are just initializing everything before running request, even if it is not needed.

Dependency Injection Container

Inversion of Control container is a tool that composes system using declarative definitions


There are 2 parts in that declarative definition

  1. Component dependencies expressed through conventions described above.
  2. Rules on how to satisfy these dependencies.


They are sometimes/most of the time called "Inversion of Control" container

In modern systems nothing is inverted, flow of control is normal, control is reversed in comparison with how systems were built in previous century.

It is actually a very simple thing

We already know how to express component dependencies through conventions


    public class FeedbackController
        {
        public FeedbackController( EmailGenerator emailGenerator, 
                                   EmailService   emailService ) {}
        }

Now we need to define rules:


    /// When application starts
    /// ...
    var emailServiceSettings = .... // load email settings

    // Define rules
    Component.For<EmailServiceSettings>().Instance( emailServiceSettings ),
    Component.For<EmailGenerator>().ImplementedBy<EmailGenerator>(),
    Component.For<EmailService>().ImplementedBy<EmailService>(),

Quick overview of Inversion of Control containers for different platforms


We are using Castle Windsor for all examples.

Application Skeleton (Console Application)


        public class MyProgram
            {
            public MyProgram( ... ) { ... }
            public static int Main( string[] args )
                {
                var container = new WindsorContainer();

                // Composition root
                container.Register(
                   Component.For<>...,
                   );

                var program = container.Resolve<MyProgram>()
                // execute root object method
                program.Run( args )
                }
        

Application Skeleton (Web Application)


        public class FeedbackController { ... }

        public class MyProgram
            {
            public Application_Start()
                {
                var container = new WindsorContainer();

                container.Register(
                   Component.For<>...,
                   Component.For<>...,
                   );

                // Request -> Framework instantiates FeedbackController
                // Need to tell to use container to instantiate controllers
                // Framework.Factory =  (Type type)=>container.Resolve( type );

                ControllerBuilder.Current.SetControllerFactory(
                     new WindsorControllerFactory() );
                }

        

Component Dependency rules in Castle Windsor

Instance



            container.Register(
                Component.For<EmailServiceSettings>().Instance( emailServiceSettings );
                );

            

Component Dependency rules in Castle Windsor

Implemented By



            container.Register(
                Component.For<IEmailLogger>().ImplementedBy<DatabaseEmailLogger>();
                );

            

Component Dependency rules in Castle Windsor

Factory Method



var dataContextFactory = DbContextFactory<DataContext>.BuildFactory( "DemoApp" );

container.Register(
    Component.For<DbContextFactory <DataContext> >().Instance( dataContextFactory ),

    Component.For<DataContext>()
             .UsingFactoryMethod( k => k.Resolve< DbContextFactory<DataContext> >().Create() )
            );

            

Lifestyles

Lifestyles control when components are created and deleted/disposed

Most common

  • Singleton
  • Transient
  • PerWebRequest

Singleton

One per application, created when needed, disposed when application finishes

  • State-less components
    
        class EmailGenerator
            {
            public EmailGenerator() {}
            public string GenerateMail( object model ) { ... }
            }
    
    
  • Components that have state that is valid through application lifetime
  • Components that are expensive to create or have around so they are shared
  • 
        class UniversityStructureRepository
            {
            public UniversityStructureRepository() {}
            public Organization[]   Organizations { get; }
            public Department[]     Departments { get; }
    
            private void Refresh() { /* read-write lock here */ }
            }
    
    

Per Web Request

One per web request, created when needed, disposed when request ends

  • Typically DataContext/ISession


    Component.For<DataContext>()
        .UsingFactoryMethod( k => k.Resolve< DbContextFactory<DataContext> >().Create() )
        .LifestylePerWebRequest()
    );

Transient

Creates new component for every dependency. Usually when dependency has state


    public class PaymentCalculator
        {
        ...
        public decimal Total { get; }
        }

Composition Root

Single place where the composition rules for application are defined


            container.Register(
                Component.For ....,
                Component.For ....,
                );

        
  1. Other parts of the system should not have a reference to IoC container
  2. All metadata about composition is available

Advanced: Declarative definitions provide useful meta data for other services

  • Visualization
  • Weaving (Aspect-Oriented Programming)
  • Analysis

Visualization

Weaving


    public class EmailService
        {
        [Transaction(Transaction.Required)]
        public virtual void SendMail( MailMessage mailMessage ) { ... }
        }

Advanced: It is OK to use Service Locator in infrastructure code


    public interface Handles<in T>  where T : IDomainEvent
        {
        void Handle(T domainEvent);
        }

    public static class DomainEvents
        {
        //Raises the given domain event
        public static void Raise<T>(T args) where T : IDomainEvent
            {
            foreach(var handler in Container.ResolveAll<Handles<T>>())
               handler.Handle(args);
            }
        }

    DomainEvents.Raise( new PaymentMadeEvent( ... ) );

    class ReservesService : Handles<PaymentMadeEvent>
        {
        void Handle( PaymentMadeEvent event ) {}
        }

References