I had to solve an interesting problem recently, so I thought I should blog about it.

If you want the tl;dr version, view the source code.

In general, you should avoid getting into a circular dependency injection situation. This means, you should not have ServiceA depend on ServiceB and ServiceB depend on ServiceA.

If you get into such a problem, it’s a sign that maybe these services should have been built as a single service, or you might want to consider the Facade pattern.

However, if you really need to have circular dependency, one way to resolve this problem is to have one of the dependency resolved during method execution.

The (original) PaymentService

Here’s a simplified version of what I was dealing with: I had to work on a PaymentService which captures incoming payments. When payments are captured, a series of events happen, such as firing off emails, triggering external API calls, deducting fees, triggering logistics, etc. — you get the idea.

Often times, this starts with some if/else or switch/case…

public class PaymentService
{
  public void Capture(Payment payment)
  {
    if (payment.Amount < 100) { // do something }
    else { // do something else }

    if (payment.Method == "cash") { // do something }
    else if (payment.Method == "paypal") { // do something else }

    if (payment.Sender.IsValidEmail) { // do something }

    // etc.
  }
}

But the list tends to grow over time. For example, I might need to add another condition to issue promotional vouchers if <insert some condition>.

So instead of having this crazy spaghetti of if/else or switch/case, the idea here is to use event handling.

Breaking down PaymentService

Start by creating a new IPaymentHandler interface…

public interface IPaymentHandler
{
  public OnCapture(Payment payment);
}

… and then implement the various classes

public class LargePaymentAmountHandler : IPaymentHandler { ... }
public class CashPaymentHandler : IPaymentHandler { ... }
public class PayPalPaymentHandler : IPaymentHandler { ... }
public class PaymentNotificationHandler : IPaymentHandler { ... }

The circular dependency

Here’s where the circular dependency occurs: PaymentService calls PaymentHandler, which processes the event, and then once successful, PaymentHandler may call PaymentService again to, for example, deduct a service fee.

Class diagram from the sample source code.

It is ideal to dependency-inject singular services like INotificationService and IPaymentService. However, avoid injecting a whole array of IPaymentHandler[] into the constructor of PaymentService. Instead, resolve them during runtime method execution. Doing so removes the circular dependency from the DI container.

This is an example of how PaymentService can get all IPaymentHandler[] within the Capture() method and then invoke all of them.

public class PaymentService : IPaymentService
{
  // injected via constructor
  public IServiceProvider ServiceProvider { get; set; }

  public void Capture(Payment payment)  
  {
    // do something...

    // fire event to all handlers
    foreach (var handler in ServiceProvider.GetServices<IPaymentHandler>())
    {
      handler.OnCapture(payment);
    }
  }
}

This is actually an adaptation of the old-skool way of event handling used in Java called Event Listeners.

A bit of the detail here: All handlers are invoked. The onus is on the event handler to process or ignore the event.

public class CashPaymentHandler : IPaymentHandler
{
  public void OnCapture(Payment payment)
  {
    if (payment.Method != "cash") return; // ignore
    // do something
  }
}

Why not use .NET Event Handling?

To use event handling in .NET, you would have to register the event handler delegate, e.g.

public class PaymentService : IPaymentService
{
  public event EventHandler PaymentCaptured;

  public void Capture(Payment payment)  
  {
     // raise the event
     PaymentCaptured?.Invoke(this, eventArgs);
  }
}
public class CashPaymentHandler : IPaymentHandler
{
  // constructor
  public CashPaymentHandler(PaymentService paymentService)
  {
    // register delegate to payment service
    paymentService.PaymentCaptured += OnCapture;
  }

  // event delegate
  public OnCapture(object sender, EventArgs e)
  {
    // do something
  }
}

Looks really neat, but we have a problem: The CashPaymentHandler class does not get instantiated until someone requests (from the DI container) for it – so the constructor is never called and the event delegate does not get registered.

You will not be able to register the event delegates in Startup.cs because these objects are instantiated at scope-level during runtime.

So instead of PaymentHandler trying to register themselves to the service, what about the service attempting to register all the event handlers during creation (i.e. in the constructor)?

Nope. It is not possible to resolve PaymentHandler in the constructor of PaymentService, because PaymentService would have been instantiated before PaymentHandler due to the dependency graph.

public class PaymentService : IPaymentService
{
  public event EventHandler PaymentCaptured;

  public PaymentService(IServiceProvider serviceProvider)  
  {
     // FAIL: This will throw an error because
     // PaymentHandler(s) depends on PaymentService
     // and we are still in the constructor
     var handlers = serviceProvider.GetServices<IPaymentHandler>();

     foreach (var handler in handlers)
     {
       PaymentCaptured += handler.OnCapture;
     }
  }
}

Why not pub/sub or message queues?

For this specific project, I had to get this out quickly based on a legacy code base. Setting up pub/sub, for example, needed considerable infrastructure planning and application re-architecture. I am a keen adopter of the KISS principle and avoid over-engineering things where necessary.

View the source code.