
{"id":4576,"date":"2019-11-08T23:09:08","date_gmt":"2019-11-09T04:09:08","guid":{"rendered":"https:\/\/ikriv.com\/blog\/?p=4576"},"modified":"2019-11-08T23:30:07","modified_gmt":"2019-11-09T04:30:07","slug":"unity-container-when-it-is-too-much-magic","status":"publish","type":"post","link":"https:\/\/ikriv.com\/blog\/?p=4576","title":{"rendered":"Unity container: when it is too much magic"},"content":{"rendered":"<p style=\"text-align: right; font-size: small;\">&#8220;<em>Any sufficiently\u00a0advanced technology is indistinguishable from magic<\/em>&#8221;<br \/>\nAthur C. Clarke<\/p>\n<p>While reviewing some code, I stumbled upon a class that receives an <code>ObservableCollection&lt;ILogFormatter&gt;<\/code> in the constructor. I discovered that this collection is never explicitly initialized. Instead, it is magically constructed by the Unity container and injected into the class.<\/p>\n<p><a href=\"https:\/\/unitycontainer.github.io\/tutorials\/Composition\/collections.html\">According to the documentation<\/a>, Unity container resolves <code>IEnumerable&lt;T&gt;<\/code> to a sequence that contains one object for each registration of type T. <code>ObservableCollection&lt;T&gt;<\/code> is not <code>IEnumerable&lt;T&gt;<\/code>, but it has a constructor that takes an <code>IEnumerable&lt;T&gt;<\/code>, so Unity resolves it recursively.<\/p>\n<p>Our code used this feature to inject a list of globally-defined formatters into the logger.<\/p>\n<pre><span style=\"color: blue;\">class<\/span> Logger\r\n{\r\n  <span style=\"color: blue;\">public<\/span> Logger(ObservableCollection&lt;ILogFormatter&gt; formatters) {...}\r\n}\r\n\r\n...\r\n<span style=\"color: blue;\">var<\/span> container = <span style=\"color: blue;\">new<\/span> UnityContainer()\r\n    .RegisterType&lt;ILogFormatter, ParenthesisFormatter&gt;(\"Parenthesis\")\r\n    .RegisterType&lt;ILogFormatter, CurrentTimeFormatter&gt;();\r\n\r\n<span style=\"color: blue;\">var<\/span> logger = container.Resolve&lt;Logger&gt;();\r\nlogger.Log(\"First message\");\r\nlogger.Log(\"Second message\");<\/pre>\n<p>This prints each message adding parenthesis, courtesy of the first formatter, and current time, thanks to the second formatter:<\/p>\n<pre><span style=\"color:brown\">03:48:43.928 (First message)\r\n03:48:43.930 (Second message)<\/span><\/pre>\n<p>Of course, DI containers are designed to &#8220;magically&#8221; construct our classes like Santa elves, but to my taste, this is too much magic. Not only the reader must be aware of this obscure feature of Unity to understand the code, he also must remember what objects can be constructed, directly or indirectly from <code>IEnumerable&lt;T&gt;<\/code>. To top it up,\u00a0 Unity treats <code>IEnumerable&lt;T&gt;<\/code> and <code>T[]<\/code> slightly differently.<\/p>\n<p>I created a <a href=\"https:\/\/github.com\/ikriv\/UnityLoggerRegistration\">sample<\/a> that mimics this code in branch &#8220;magic&#8221;, and a refactored version where formatter registration is explicit in branch &#8220;master&#8221;. The refactored code is larger, but is much easier to understand and contains less magic.\u00a0 In refactored populating the list of formatters is explicit:<\/p>\n<pre><span style=\"color: blue;\">class<\/span> DefaultFormatterList \r\n{\r\n  <span style=\"color: blue;\">public<\/span> DefaultFormattersList Add&lt;T&gt;() <span style=\"color: blue;\">where<\/span> T : ILogFormatter;\r\n  <span style=\"color: blue;\">public<\/span> ReadOnlyCollection&lt;ILogFormatter&gt; FormattersList { <span style=\"color: blue;\">get<\/span>; }\r\n}\r\n\r\ncontainer.Resolve&lt;DefaultFormattersList&gt;()\r\n  .Add&lt;ParenthesisFormatter&gt;()\r\n  .Add&lt;CurrentTimeFormatter&gt;();<\/pre>\n<p>Also, in the spirit of the single responsibility principle <code>LoggerWithFormatters<\/code> is a decorator: it takes a plain logger in the constructor and adds the formatters to it:<\/p>\n<pre><span style=\"color: blue;\">class<\/span> LoggerWithFormatters : ILogger\r\n{\r\n  <span style=\"color: blue;\">public<\/span> LoggerWithFormatters(\r\n    ILogger upstream, \r\n    IReadOnlyCollection&lt;ILogFormatter&gt; formatters);\r\n}<\/pre>\n<p>Container registration then looks as follows:<\/p>\n<pre>container\r\n  .RegisterType&lt;DefaultFormattersList&gt;(new ContainerControlledLifetimeManager())\r\n  .RegisterFactory&lt;ILogger&gt;(c =&gt;\r\n       <span style=\"color: blue;\">new<\/span> LoggerWithFormatters(\r\n       c.Resolve&lt;ConsoleLogger&gt;(),\r\n       c.Resolve&lt;DefaultFormattersList&gt;().FormattersList));<\/pre>\n<p>This may look more complicated then the original, but it tells us exactly what is going on: we resolve ILogger interface by creating an instance of <code>LoggerWithFormatters<\/code> and passing some parameters to it.<\/p>\n<p>By replacing &#8220;magic&#8221; container feature with explicit formatter list, we achieved a number of important advantages:<\/p>\n<ul>\n<li>No more astonishment, &#8220;how this can possibly work?&#8221;. This is somewhat subjective, but the feature of the container to supply a collection is not very well known.<\/li>\n<li>The collection of formatters can be easily examined and debugged. If we use container regisrations, the collection of formatters is buried somewhere inside the container&#8217;s private fields.<\/li>\n<li>We can add locking, sorting, logging (&#8220;added formater XYZ&#8221;), etc. to the formatters collection<\/li>\n<li>No more redundant registration names<\/li>\n<\/ul>\n<p>The moral of the story is that at times relying on the obscure features of the library makes the code difficult to maintain and reason about. Even if a feature is there, it does not mean it necessarily must be used.<\/p>\n<p>PS. <code>ObservableCollection&lt;T&gt;<\/code> actually has two 1-parameter constructors: one accepting <code>IEnumerable&lt;T&gt;<\/code>and one taking <code>List&lt;T&gt;<\/code>. Older versions of Unity would refuse to construct such as class due to ambiguity, but the modern version just takes the first suitable constructor in the order of declaration, which happens to be <code>IEnumerable&lt;T&gt;<\/code>. However, even if it were the the <code>List&lt;T&gt;<\/code>constructor, the result would have been the same, since <code>List&lt;T&gt;<\/code> has a constructor that takes <code>IEnumerable&lt;T&gt;<\/code>. On the other hand, if the other constructor accepted <code>T[]<\/code>, the result would have been different, and hard to debug, as unlike the enumerable, the array does not include the implicit (unnamed) type registration.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>&#8220;Any sufficiently\u00a0advanced technology is indistinguishable from magic&#8221; Athur C. Clarke While reviewing some code, I stumbled upon a class that receives an ObservableCollection&lt;ILogFormatter&gt; in the constructor. I discovered that this <a href=\"https:\/\/ikriv.com\/blog\/?p=4576\" class=\"more-link\">[&hellip;]<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"Layout":"","footnotes":""},"categories":[3,8,4,5],"tags":[],"class_list":["entry","author-ikriv","post-4576","post","type-post","status-publish","format-standard","category-dotnet","category-cs","category-hack","category-dev"],"_links":{"self":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4576","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4576"}],"version-history":[{"count":5,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4576\/revisions"}],"predecessor-version":[{"id":4592,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/4576\/revisions\/4592"}],"wp:attachment":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4576"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4576"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4576"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}