How to inject proper dependency based on constructor parameter name
Asked Answered
S

2

6

I have this interface that is being used by a handful of concrete types, such as EmailFormatter, TextMessageFormatter etc.

public interface IFormatter<T>
{
    T Format(CompletedItem completedItem);
}

The issue I'm having is that with my EmailNotificationService is that I want to inject EmailFormatter. The constructor signature for this service is public EmailNotificationService(IFormatter<string> emailFormatter).

I'm pretty sure I've seen this done before but how do I register this with Windsor so that it injects EmailFormatter if the constructor parameter name is emailFormatter?

Here is my Windsor registration code.

container.Register(Component.For<IFormatter<string>>().ImplementedBy<EmailFormatter>());
Sanctitude answered 7/8, 2011 at 15:14 Comment(0)
A
6

Service code:

public EmailNotificationService(IFormatter<string> emailFormatter){...}

Dependency registration code:

container.Register(
    Component.For<IFormatter<string>().ImplementedBy<TextMessageFormatter>().Named("TextMessageFormatter"),
    Component.For<IFormatter<string>().ImplementedBy<EmailFormatter>().Named("EmailFormatter"),
    Component.For<INotificationService>().ImplementedBy<EmailNotificationService>().ServiceOverrrides(
        ServiceOverride.ForKey("emailFormatter").Eq("EmailFormatter"))
);
Agma answered 7/8, 2011 at 17:22 Comment(1)
Thanks for the answer. I was searching for the wrong things. :DSanctitude
G
9

Don't try to solve this problem in the DI configuration. Instead, solve it in the design of the application. It seems to me that you have defined several distinct things with the same interface. Your requirements makes it pretty obvious, since you say:

I want to inject EmailFormatter

You don't want to inject a formatter; you want to inject an e-mail formatter. In other words, you are violating the Liskov Substitution Principle. Fix this issue in the application. Define an IEmailFormatter interface and let the EmailNotificationService depend on this:

public interface IEmailFormatter
{
    string Format(CompletedItem completedItem);
}

public class EmailNotificationService
{
    public EmailNotificationService(IEmailFormatter formatter)
    {
    }
}

This has two important advantages:

  1. It makes the code more maintainable, since now it is clear what kind of dependency EmailNotificationService really has.
  2. It makes the DI configuration much easier and more maintainable. Just look at the dependency registration of Zach's answer, and you'll understand what I'm talking about.
Gailgaile answered 7/8, 2011 at 20:8 Comment(2)
I understand with what you mean but isn't that a little redundant by having separate interfaces that all do one thing, format content?Sanctitude
@User: Having the EmailNotificationService depend on an IFormatter<string> implies that any string formatter would do just fine, while it can only work correctly with a formatter that can output mails. So what the EmailNotificationService is concerned it is not the same. While the IMailFormatter and the IFormatter<string> have the same input and output types, the contract of the IMailFormatter is stronger, since it explicitly says what kind of string it returns, while IFormatter<string> could return anything.Gailgaile
A
6

Service code:

public EmailNotificationService(IFormatter<string> emailFormatter){...}

Dependency registration code:

container.Register(
    Component.For<IFormatter<string>().ImplementedBy<TextMessageFormatter>().Named("TextMessageFormatter"),
    Component.For<IFormatter<string>().ImplementedBy<EmailFormatter>().Named("EmailFormatter"),
    Component.For<INotificationService>().ImplementedBy<EmailNotificationService>().ServiceOverrrides(
        ServiceOverride.ForKey("emailFormatter").Eq("EmailFormatter"))
);
Agma answered 7/8, 2011 at 17:22 Comment(1)
Thanks for the answer. I was searching for the wrong things. :DSanctitude

© 2022 - 2024 — McMap. All rights reserved.