Reputation: 327
As I am learning about Dependency Injection, so I have also shared my understanding (Please correct me wherever you guys feel to do so). The concept behind the following sample is to check the advantage of using Dependency Injection as it helps in implementing loose coupling in the application which will further prevent me from making lots of changes in the project in the case when concrete definitions (classes) tend to change in future.
IEmailService - Interface:
public interface IEmailService
{
void SendMail();
}
EmailService - Class inheriting above interface
public class EmailService : IEmailService
{
public EmailService(string emailFrom, string emailTo)
{
}
public void SendMail()
{
// Code here
}
}
HomeController
public class HomeController : Controller
{
private IEmailService _service;
public HomeController(IEmailService service)
{
_service = service;
}
public IActionResult Index()
{
_service.SendMail();
return View();
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
...
services.AddTransient<IEmailService, EmailService>();
...
}
Practical assumption
I assume that earlier there was no parameterized constructor in the EmailService class, but in future, I feel like I need to add a parameterized constructor but it shouldn't have an impact on those controllers (like HomeController) which are using abstraction (interfaces) to access them indirectly.
Unfortunately, when I am running the above code, I am getting the following exception which seems to disappear if I am removing the parameterized constructor from EmailService class.
InvalidOperationException: Unable to resolve service for type 'System.String' while attempting to activate 'DependencyInjectionDemo.Services.EmailService'.
Upvotes: 0
Views: 824
Reputation: 172835
You can register your EmailService
using a lambda:
services.AddTransient<IEmailService>(_ => new EmailService("from@", "to@"));
emailFrom
and emailTo
however seem runtime data, which means that the Controller might be responsible of supplying this information to the IEmailService
. Since the EmailService
is decoupled from the controller, it means that the controller is not responsible of its creation.
In general, you should prevent needing to initialize your components (EmailService
in your case) with runtime data, as explained here, the advice is:
Don't inject runtime data into application components during construction; it causes ambiguity, complicates the composition root with an extra responsibility and makes it extraordinarily hard to verify the correctness of your DI configuration. My advice is to let runtime data flow through the method calls of constructed object graphs.
In your case this basically means changing the IEmailService
abstraction to the following:
public interface IEmailService
{
void SendMail(string emailFrom, string emailTo);
}
Upvotes: 4