0

I have defined an interface IEmailManager as having send method. The email controller is using its concrete type EmailManager as a constructor dependency that sends the email.

public interface IEmailManager
{
    void SendEmail();
}

public class EmailManager : IEmailManager
{
    private readonly SmtpCredentials _smtpCreds;

    public EmailManager(SmtpCredentials smtpCreds)
    {
        _smtpCreds = smtpCreds;
    }

    public void SendEmail()
    {
    }
}

I injected the concrete type using dependency injection in .NET Core:

builder.Services.AddSingleton<IEmailManager, EmailManager>();

Now there is a requirement to send a different type of email SummaryEmailManager. I created a new type called SummaryEmailManager that will implement the same interface.

public class SummaryEmailManager : IEmailManager
{
    private readonly SmtpCredentials _smtpCreds;

    public SummaryEmailManager(SmtpCredentials smtpCreds)
    {
        _smtpCreds = smtpCreds;
    }

    public void SendEmail()
    {
    }
}

I can implement it in the following two ways please suggest the best suitable one or you may suggest a different best approach.

I can use dependency Injection to add a Singleton for both concrete types in .NET 6, just like the following code in program.cs.

builder.Services.AddSingleton(Configuration.GetSection("SmtpSettings").Get<SmtpCredentials>());
builder.Services.AddSingleton<IEmailManager, EmailManager>();
builder.Services.AddSingleton<IEmailManager, SummaryEmailManager>();

public EmailController(IEnumerable<IEmailManager> emailManagers)
{}

Or I can create IEmailManagerFactory and create its concrete type as EmailManagerFactory that will return the IEmailManager on the basis of switch case / condition?

public interface IEmailManagerFactory
{
    IEmailManager Create();
}

public class EmailManagerFactory : IEmailManagerFactory
{
    public IEmailManager Create(string type)
    {
        switch (type)
        {
            case "Summary":
                return new EmailManager(_smtpCreds);

            default:
                return new SummaryEmailManager(_smtpCreds);
        }
    }
}

And use this factory to be injected via DI into EmailController?

buidler.Services.AddSingleton<IEmailManagerFactory, EmailManagerFactory>();

public EmailController(IEmailManagerFactory emailManagerFactory)
{
   
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Engr Umair
  • 130
  • 10

1 Answers1

0

Factory is better.
1.IEnumerable will let you create all services instances, its a waste.
2.Factory mode is better for unit-testing. You can test the switching function indedently
3.Factory mode is more compliance to Reduce code coupling
4.You don't have to care about how to find the right service in the enumerable when using in controller.First?Last?How about more services? you have to create a name for each manager to select like

    public class EmailManager : IEmailManager
    {
        public string Name { get; set; }
        public EmailManager()
        {
            Name = "defaultmanager";
        }

Then use in the controller

emailManagers.SingleOrDefault(x => x.Name == "defaultmanager").SendEmail();

It doesn't seems to be convinient.
...

IEnumerable maybe suitable when you want different DI lifetime(singleton/transient/scoped).

Qiang Fu
  • 1,401
  • 1
  • 2
  • 8