C# dynamic – A friend you may want to keep a distance

Posted by

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 retro-steps and it was difficult to pinpoint what was causing the issue. It took me sometime to figure out the root cause of the issue and it turned out to be quite an interesting issue.

Background

As part of my PR, I had added a new code to map a Repository 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:

The AbsractBaseRepository was then used by one of the nested child Repository as below:

The 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 Dto from 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 repository object to the derived repository and call the correct overload of ToDto method.

The Issue

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.

The Fix

As a fix for this issue, I decided to go back to basics and use explicit conversion to map the Repository and 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:

Lessons Learnt

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.

Photo by Lewis Ngugi on Unsplash

Advertisements