Description: dependency injection2e.jpg

From Dependency Injection, Principles, Practices, and Patterns by Steven van Deursen and Mark Seemann

This article delves into the Method Injection DI Pattern: how it works, and when and why you might want to use it.


Take 37% off Dependency Injection, Principles, Practices, and Patterns. Just enter code fccseemann into the discount code box at checkout at manning.com.


How can we inject a Dependency into a class when it’s different for each operation? By supplying it as a method parameter.

In cases where a Dependency can vary with each method call, or the consumer of such a Dependency can vary on each call, you can supply a Dependency via a method parameter.

Definition

Method Injection supplies a consumer with a Dependency by passing it as method argument on a method called outside the Composition Root.

The caller supplies the Dependency as a method parameter in each method call. Take the following ApplyDiscountFor method of a Product Entity for example, where the method accepts an IUserContext Dependency using Method Injection:

IUserContext presents contextual information for the operation to run, which is a common scenario for Method Injection. Often this context is supplied to a method alongside a “proper” value, as shown in listing 1.

Listing 1  Passing a Dependency alongside a proper value

  
 public decimal CalculateDiscountPrice(decimal price, IUserContext context)
 {
     if (context == null) throw new ArgumentNullException("context");
     decimal discount = context.IsInRole(Role.PreferredCustomer) ? .95m : 1;
     return price * discount;
 }
  

The price value parameter represents the value on which the method is supposed to operate, whereas context contains information about the current context of the operation; in this case, information about the current user. The caller supplies the Dependency to the method. The Guard Clause guarantees that the context’s available to the rest of the method body.

Method Injection is different from other types of DI patterns in that the injection doesn’t happen in a Composition Root but, rather, dynamically at invocation. This allows the caller to provide an operation-specific context. Table 1 provides a summary of the advantages and disadvantages of Method Injection.

Table 1  Method Injection advantages and disadvantages

Advantages

Disadvantages

Allows the caller to provide operation-specific context

  • Allows injecting Dependencies into data-centric objects that aren’t created inside the Composition Root

Limited applicability

  • Causes the Dependency to become part of the public API of a class or its abstraction

There are two typical use cases for applying Method Injection:

  • When the consumer of the injected Dependency varies on each call
  • When the injected Dependency varies on each call to a consumer

Listing 1 is an example of how the consumer varies. This is the most common form, which is why we’ll provide another example in this article.

Example: Varying the Dependency’s consumer on each method call

When you practice Domain-Driven Design, it’s common to create domain Entities that contain domain logic, effectively mixing runtime data with behavior in the same class. Entities are typically not created within the Composition Root. Take the following Customer Entity, for example.

Listing 2  An entity containing domain logic but no Dependencies (yet)

  
 public class Customer                               
 {
     public Guid Id { get; private set; }            
     public string Name { get; private set; }        
  
     public Customer(Guid id, string name)           
     {
         ...
     }
  
     public void RedeemVoucher(Voucher voucher) ...  
  
  
     public void MakePreferred() ...                 
 }
  

A domain Entity.

The Entity‘s data members. This is the application’s runtime data.

The constructor requires the Entity‘s data to be supplied. This way the constructor can ensure the Entity is always created in a valid state.

Lets the customer redeem a voucher.

Promotes the customer to one with the status Preferred.

The RedeemVoucher and MakePreferred methods in listing 2 are domain methods. RedeemVoucher implements the domain logic that lets the customer redeem a voucher. MakePreferred, on the other hand, implements the domain logic that promotes the customer. A regular customer could get upgraded to become a preferred customer, which might give certain advantages and discounts, similar to being a frequent flyer airline customer.

Entities that contain behavior besides their usual set of data members would easily get a wide range of methods, each requiring their own Dependencies. Although you might be tempted to use Constructor Injection to inject such Dependencies, that leads to a situation where each such Entity needs to be created with all of its Dependencies, even though only a few may be necessary for a given use case. This complicates testing the logic of an Entity, because all Dependencies need to be supplied to the constructor, even though a test might only be interested in a few Dependencies. Method Injection, as shown in the next listing, offers a better alternative.

Listing 3 An Entity using Method Injection

  
 public class Customer
 {
     public Guid Id { get; private set; }
     public string Name { get; private set; }
  
     public Customer(Guid id, string name)
     {
         ...
     }
  
     public void RedeemVoucher(                            
         Voucher voucher,
         IVoucherRedemptionService service)
     {
         if (voucher == null) throw new ArgumentNullException("voucher");
         if (service == null) throw new ArgumentNullException("service");
  
         service.ApplyRedemptionForCustomer(voucher, this.Id);
     }
  
     public void MakePreferred(IEventHandler handler)      
     {
         if (handler == null) throw new ArgumentNullException("handler");
  
         handler.Publish(new CustomerMadePreferred(this.Id));
     }
 }
  

Using Method Injection, both Entity‘s domain methods, RedeemVoucher and MakePreferred, accept the required DependenciesIVoucherRedemptionService and IEventHandler. They validate the parameters and use the supplied Dependency.

Inside a CustomerServices component, the Customer’s RedeemVoucher method can be called while passing the IVoucherRedemptionService Dependency with the call, as shown next.

Listing 4  A component using Method Injection to pass a Dependency

  
public class CustomerServices : ICustomerServices
{
    private readonly ICustomerRepository repository;
    private readonly IVoucherRedemptionService service;

    public CustomerServices(                                   
        ICustomerRepository repository,                           
        IVoucherRedemptionService service)                         
    {
        this.repository = repository;
        this.service = service;
    }

    public void RedeemVoucher(
        Guid customerId, Voucher voucher)
    {
        var customer =
            this.repository.GetById(customerId);

        customer.RedeemVoucher(voucher, this.service);         

        this.repository.Save(customer);
    }
}

The CustomerServices class uses Constructor Injection to statically define its required Dependencies. IVoucherRedemptionService is one of those Dependencies.

The IVoucherRedemptionService Dependency is passed to an already constructed Customer Entity using Method Injection. Customer is created inside the ICustomerRepository implementation.

In listing 4, only a single Customer instance is requested from ICustomerRepository. But a single CustomerServices instance can be called over and over again using a multitude of customers and vouchers, causing the same IVoucherRedemptionService to be supplied to many different Customer instances. Customer is the consumer of the IVoucherRedemptionService Dependency and, while you’re reusing the Dependency, you’re varying the consumer.

Unlike Constructor Injection and Property Injection, you mainly use Method Injection when you want to supply Dependencies to an already existing consumer. With Constructor Injection and Property Injection, on the other hand, you supply Dependencies to a consumer while it’s being created.


If you want to know more about Method Injection, you’ll have to get a copy of the book! Go see it on liveBook here now!

You can also see other articles on common DI-related topics:
DI intro
Composition Root
Abstract Factories
Method Injection
Service Locator Anti-Pattern
Ambient Context Anti-Pattern
Constructor Injection