Why all DI frameworks check for all dependency in runtime before application start? Why not to check that in compile-time, what are benefits for that?
Is it possible to change dependency in deployed application? For example when application started it sends notification through email, but after can we change to send through sms without stopping application?
DI Containers by definition work by using reflection which means runtime metadata. If you want everything to be checked at compile-time, you should not use a DI Container. In that case there are other options.
When you require everything to be wired at compile time, you can either use a practice called Pure DI, which means you hand-wire your dependencies in the application's Composition Root using the new keyword of your language.
The second option is to use a tool or process that uses a post-compilation process to generate these object graphs (the chain of classes that contain behavior with their dependencies) and their new keywords. From a definition's point of view, those tools are not 'DI Containers' because they don't depend on runtime metadata. What they offer is a code generation process, with a syntax that has some similarity to DI Containers, while their generated code is similar to that of Pure DI. Compared to DI Containers they are limited in flexibility, but they are useful in scenarios where reflection is too costly -or simply unavailable- or the application's constraints in memory usage prevents the use of a DI Container as runtime.
but after can we change to send through sms without stopping application?
You seem to require the application to respond to configuration changes while keeping running. There are common patterns that allow you to achieve this. Typically you want to define that application's component object graph once. Although you might create such graph per request, in my experience, it's better to ensure the shape of the graph doesn't change later on. Instead, you can use patterns such as Proxy and Composite. For instance considering the following interface and implementations:
interface INotificationSender
{
void Send(string message);
}
class MailNotificationSender : INotificationSender { ... }
class SmsNotificationSender : INotificationSender { ... }
We can define a Proxy as follows:
class ConfigBasedNotificationSenderProxy : INotificationSender
{
private readonly INotificationSender mail;
private readonly INotificationSender sms;
public ConfigBasedNotificationSenderProxy(
INotificationSender mail, INotificationSender sms)
{
this.mail = mail;
this.sms = sms;
}
public void Send(string message) => GetConfiguredSender().Send(message);
private INotificationSender GetConfiguredSender()
{
bool sendThroughMail = ReadFromConfgWhereToSendTo();
return sendThroughMail ? this.mail : this.sms;
}
}
You can build the following object graph at application startup:
var sender =
new ConfigBasedNotificationSenderProxy(
mail: new MailNotificationSender(...)
sms: new SmsNotificationSender(...));
You can wire this with a DI Container as well, using a 'DI code generator', or do it by hand (Pure DI) as the previous snippet shows. Wiring this using Pure DI is trivial, while your millage might vary depending on the tool you use.
Important point however that I'm trying to make here is, that even if you need to change how the application behaves at runtime, you don't need to rewire your application. You should be able to change the call graph that goes through the application's object graphs by using Proxy implementations.
This is different to the ability to change the configuration only at startup. In that case the composition of your object graphs can be as follows:
var config = ReadConfigurationFile();
INotificationSender sender = config["mail"] == "true"
? new MailNotificationSender(...)
? new SmsNotificationSender(...));
var consumer = new SomeConsumer(sender);
In this case, the ConfigBasedNotificationSenderProxy isn't required, because only one implementation will be used while the application is running. Changing how notification are sent, in this case, means changing the configuration file and, therefore, restarting the application. I have too little experience with 'DI code generators' to describe how they would handle this last scenario.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With