I have already written about the “principle of least surprise” applied to API design and how Microsoft’s .Net Framework developers themselves ignore their own recommendations.
I would not care less if their violations did not waste my time, but they do.
Last one comes with a very simple and, I suppose, very common scenario: dealing with cookies. In my case I was reading the value of a cookie in one control and performing some operations depending of its value, but that given cookie could get modified by another control in another stage of the request.
Piece of cake if it was a different request, as common sense says one would read from the HttpCookieCollection coming from the property Cookies from the current HttpRequest instance and write any update /new cookie to the HttpCookieCollection available in the property Cookies of the current HttpResponse.
But as it is the same request, we have to write to the response collection and try to read first from the response collection of cookies and, in the case that read was not successful, read from the cookies of the request.
Simple code, passing unit tests and empty value of the cookie in the subsequent request. What? You have to be kidding me. How can that be possible? Digging with the debugger (mind I told you unit tests were passing, but of course unit tests only mimic one request) I find that after reading from the collection of response cookies using the indexer, a new empty cookie gets added, which of course leaves the field ready for the next request odd behavior.
Quick scan of the MSDN documentation of the indexer and no mention of side effects. How could one expect side effects in an indexer? No one in their right mind would model a class with such interface. Let’s use the Get() method. This time for sure no one would mix commands and queries in a method with such name. Doh!! Same behavior. Quick scan of the MSDN documentation of the method and the remarks says that a new cookie will get added to the collection if it does not exist!! WTF!! But I had my code working when I was only reading from the request collection of cookies!!
Someone is lying. Calm. Fire Reflector. This one never lies.
It turns out that MSDN documentation of the Get() method is actually lying and the one of the indexer is not telling the whole truth. The violation of common sense and misleading method names only occurs when the collection is created using an internal constructor, which is used ONLY when creating the cookie collection for the response (not for the request or from the outside world –tests-).
A closer look to the MSDN and I find a community content stating exactly what had taken me 1-odd hour to find out by myself.
Thank you my beloved framework developers by breaking all common sense rules in API design by having serious side-effects in query-methods. I know that rules and best-practices (or even laws of Newtonian physics) do not apply in Redmon, or wherever this evil piece was conceived. What were they thinking?
I have the suspicion that this behavior was inherited from “classic” ASP and they chose not to fix it to ease the transition of code that took “advantage” of this behavior.
But come one! We have come a loooong way since 1.0. Platform has matured a lot and we have seen breaking changes in the process. Please, Microsoft, keep innovating but do not let dirt under your own rug. And fix monstrosities like this one in the process. Developers are mature people and would understand why they have to change logic that was coded “around” if they want to take advantage of new and juicy features. Besides migrating from classic Asp does not make the highlights anymore, let’s learn from Internet Explorer that carrying a lot of poisonous baggage is bad for your back.
By the way, I solved it, coding around the issue with nasty, hideous code that queries the AllKeys array :-(