Recently, after one of my PRs which was merged to master, my teammates started complaining about a weird scenario. On some occasions, the ASP.NET Core app hosted inside the IIS worker process (w3wp.exe), would simply die without any exception/ warning. There were no clear repro-steps and it was difficult to pinpoint what was causing the issue. It took me some time to figure out the root cause of the issue and it turned out to be quite an interesting issue.
As part of my PR, I had added a new code to map a
object/ entity to a
Dto. It was quite complex and a multi-level nested
entity. I did not use the library such as
Automapper to automatically map the entities. Why I chose to do mapping manually could be a discussion for another day. Anyways, the culprit code went something like this.
We had an
abstract base class, let us call it
AbsractBaseRepository and this class was derived by as many as 18 different child classes. Something similar to below:
AbsractBaseRepository was then used by one of the nested child Repository as below:
Dto which was mapped from this
Repository had a similar structure.
Why and how we ended up with this structure is again out-of-scope of this post. To map the
Repository, I tried to be a little bit smart/ lazy and used C#
dynamic as below:
In the above code, casting the
repository parameter to
dynamic implicitly converted the base
to the derivedrepository
and call the correct overload ofToDto` method.
Unfortunately, I turned to be too smart for my own good. I missed the mappings for one of the derived
Repository class. In a scenario where the missed derived
Repository was present in the
entity, the code execution fall-back to base class overload method, that is,
ToDto(SomeAbstractRepository repository). This resulted in an infinite loop causing the process to crash during debugging. Since there were as many as 18 derived
Repository classes this was somehow missed in integration and unit tests as well. The easiest way to fix this was to simply add the mapping for the missed
Repository class. However, it presented an additional risk, what if we add another derived Repository and we miss adding the mapping for that Repository? In that scenario, we would land up in a similar situation.
As a fix for this issue, I decided to go back to basics and use explicit conversion to map the
Dto, even if it meant more lines of code. The explicit conversion helped us to identify the issue at the compile-time or in the worst case throw a clear exception at run-time instead of blowing the entire process without any exception at the run-time. The updated code looked something like below:
An important lesson which I learned while resolving this issue was to be extra cautious while using
dynamic. For all the power
dynamic brings, it comes at a cost. Personally, I try to avoid
dynamic as much as possible and this issue just gave another reason why I would continue to do so.