ASP.Net Core – Resolving Circular References

.net coreasp.net-corelogging

I was working on migrating over a project which uses a static logger, and a static email service.
The email service logs emails sent, and the logger service sends an email if there are any logging issues (like DB down, etc.). If the email service fails, it can also log the issue.

If the logger fails, it tries to send an email notice, but if the email service fails (which could be why the logger was called in the first place), it just continues on and writes the issue to a log file.

Email Service <--statically references--> Logger Service

The issue is that both "services" are singletons, and both would need reference to each other as a constructor parameter. My first thought was property based injection, but I quickly found that seems to be a Temporal Coupling situation that is much undesired.

The main running idea now is, at the log level, have it call the service provider to create an email service (so it will get the singleton instance), but I was really trying to keep the class library also decoupled from the .Net Core injection system. Anyone have any recommendations in case I'm missing something?

Edit: After more thought, does it make sense perhaps to create an abstracted service provider of my own (a basic one) that can have another service injected into it? Then I could perhaps just pass that around instead in such cases.

Best Answer

I suggest you to read a great book about the C# dependency injection by Mark Seemann, with a lot of real world samples for different tasks you can do with it, even with circular dependencies similar to your case.

In general, the main design rule is You can't resolve the problem on its level, and in this case it's true too. You really need a mediator here to resolve interactions between two loggers. It easily can save the state for both of them, and notify each other about changes.

For example, you can introduce some Target for the messages, like this:

GeneralMessage // both loggers got this
EmailMessage // only email logger got this
LoggerMessage // only logger got this

So, in case of some trouble with one of your loggers, mediator send the message with corresponding type to notify other about issue.

Common approach for such cutting-edge logic is an AOP implementation (personally I prefer the PostSharp, which provides you a general approach, even with conventional techniques to assign the aspects on your classes).

As for decoupling your code from the .Net Core injection system, I suggest you to examine the Logger Factory, which is:

The new framework creates an abstraction layer or wrapper that enables you to use whichever logging framework you want as a provider. This ensures you have the maximum flexibility in your work as a developer. Furthermore, even though it’s only available with .NET Core, referencing .NET Core NuGet packages like Microsoft.Extensions.Logging for a standard Visual Studio .NET 4.6 project is no problem.

With Logger Factory you can even filter the messages to notify the all loggers about problems with other ones, connect to OS event logs and much more.

Related Topic