dI.Hook - Creating a dependency injection container

3 minute read

If you are new to dI.Hook, please visit the di.Hook Product Page for Overview, Release Version and Source Code.

Some posts you might want to read would be:

  1. How to create a HookRepository?
  2. How to add hooks to HookRepository?
  3. How to add hooks via configuration file to a HookRepository?
  4. How to invoke all hooks using dI.Hook?
  5. How to invoke hooks that satisfy a condition?
  6. How to retrieve an object of hook?
  7. How to remove hooks from HookRepository?
  8. How to ignore hook on specific method?

So this is the ninth article in the series which will deal with creating a dependency injection container and using them in your applications

Creating a dependency container is very simple.  You can either choose a default implementation of Container, or you can implement the interface IContainer and write your own definition of container.    The implementation you would require to implement would be

  1. public interface IContainer
  2. {
  3.     ReadOnlyCollection<dIHook.Objects.ContainerItem> Collection { get; }
  4.     ReadOnlyCollection<Guid> Keys { get; }
  5.     void Register<TClass>() where TClass : class;
  6.     void Register<TClass>(Guid key) where TClass : class;
  7.     void Register<TInterface, TImplementation>() where TImplementation : class, TInterface;
  8.     void Register<TInterface, TImplementation>(Guid guid) where TImplementation : class, TInterface;
  9.     void Register<TInterface>(Guid key, TInterface objectValue);
  10.     void Register<TInterface>(TInterface objectValue);
  11.     TInterface Resolve<TInterface>() where TInterface : class;
  12.     TInterface Resolve<TInterface>(Guid key) where TInterface : class;
  13.     ReadOnlyCollection<Type> Types { get; }
  14. }


Assuming that you are not implementing this interface and are using the default implementation provided with dI.Hook, you would involve having

  1. Some business interfaces and entities
  2. Container having a definition of interface and implemented entities
  3. Container resolving these entity objects

For the sake of understanding, let’s have a eCommerce case study where we have 3 interfaces –

  • ICustomer – Represents customer and his order
  • INotifier – Notification service for the customer
  • IBillingProcessor – Processor that takes the payment type and processes the billing

So let’s quickly set the stage with these interface and their implementation

  1. public interface IBillingProcessor
  2. {
  3.     void ProcessPayment(OnlineOrder order);
  4. }
  5. public class BillingProcessor : IBillingProcessor
  6. {
  7.     private PaymentType _paymentType;
  8.     public BillingProcessor(PaymentType paymentType) {}
  9.     public void ProcessPayment(OnlineOrder order) { }
  10. }
  11. public enum PaymentType { Cash,CreditCard}
  12. public interface ICustomer
  13. {
  14.     void UpdateCustomerOrder(int customerId, string product);
  15. }
  16. public class InternetCustomer : ICustomer
  17. {
  18.     public InternetCustomer() { }
  19.     public void UpdateCustomerOrder(int customerId, string product) { }
  20. }
  21. public interface INotifier
  22. {
  23.     void SendReceipt(OnlineOrder order);
  24. }
  25. public class EmailNotifer : INotifier
  26. {
  27.     public EmailNotifer() { }
  28.     public void SendReceipt(OnlineOrder order) { }
  29. }


      Now, let’s use the container with these business entities

      1. Container container = new Container();
      2. container.Register<IBillingProcessor, BillingProcessor>();
      3. container.Register<ICustomer, InternetCustomer>();
      4. container.Register<INotifier, EmailNotifer>();


      The above code creates a default objects of the BillingProcessor, InternetCustomer and EmailNotifier.  If you want to pass your own object you could write as below (see BillingProcessor object)

      1. Container container = new Container();
      2. BillingProcessor billingProcessor = new BillingProcessor(PaymentType.CreditCard);
      3. container.Register<IBillingProcessor>(billingProcessor);
      4. container.Register<ICustomer, InternetCustomer>();
      5. container.Register<INotifier, EmailNotifer>();


      Now let’s place an order on an eCommerce website using this container.  An eCommerce class would be of no use without a reference to ICustomer, IBillingProcessor and INotifier.  So eCommerce class for this example would be

      1. public class ECommerce
      2. {
      3.     public void Process(OnlineOrder order)
      4.     {
      5.         _BillingProcessor.ProcessPayment(order);
      6.         _Customer.UpdateCustomerOrder(order.CustomerId,
      7.             order.Product);
      8.         _Notifier.SendReceipt(order);
      10.         Debug.WriteLine("Process called");
      11.     }
      13.     public ECommerce(IBillingProcessor billingProcessor,
      14.                 ICustomer customer,
      15.                 INotifier notifier)
      16.     {
      17.         _BillingProcessor = billingProcessor;
      18.         _Customer = customer;
      19.         _Notifier = notifier;
      21.     }
      23.     IBillingProcessor _BillingProcessor;
      24.     ICustomer _Customer;
      25.     INotifier _Notifier;
      26. }


      So your eCommerce takes objects of ICustomer, IBillingProcessor and INotifier in the constructor and in the Process method, it

      1. Calls BillingProcessor to ProcessPayment for the order
      2. Calls Customer to UpdateCustomerOrder
      3. Calls Notifier to send notification

      Now let’s see how this can be invoked using dI.Hook container that we created

      1. OnlineOrder onlineOrder = new OnlineOrder()
      2. {
      3.     CustomerId = 12212,
      4.     EmailAddress = "dihook@ganshani.com",
      5.     Price = 400,
      6.     Product = "NewProduct"
      7. };
      8. ECommerce commerce = container.Resolve<ECommerce>();
      9. commerce.Process(onlineOrder);

      The above code creates an object of ECommerce class.  Now what is important here is to see what internally happens.  When you call Resolve method, it tries creating an object of ECommerce class.  It finds that its constructor requires objects of IBillingProcessor, ICustomer and INotifier.  Now since you have already registered objects of these interfaces the dI.Hook Container retrieves the object from the collection and then will try to create an object of ECommerce.  If, however, ECommerce constructor required an un-registered object it will try to create a default object and add it in the container.

      So if you explicitly created an object of BillingProcessor with payment type as CreditCard and added them in Container, it will use that object; otherwise, it will use default object of BillingProcessor with payment type as Cash

      dI.Hook Container ensures that it resolves the hierarchical dependencies automatically.  However if it is unable to resolve for any reason, it will throw an exception.