
{"id":2539,"date":"2018-10-17T23:16:05","date_gmt":"2018-10-18T03:16:05","guid":{"rendered":"https:\/\/ikriv.com\/blog\/?p=2539"},"modified":"2018-10-17T23:16:05","modified_gmt":"2018-10-18T03:16:05","slug":"stack-overflow-in-unity-di-container-when-registering-a-decorator","status":"publish","type":"post","link":"https:\/\/ikriv.com\/blog\/?p=2539","title":{"rendered":"Stack overflow in Unity DI Container when registering a Decorator"},"content":{"rendered":"<p>After upgrading to a newer version of Unity, we found that our process began to fail with <code>StackOverflowException<\/code>. Investigation showed that it happened because the following registration behaves differently depending on the version of Unity:<\/p>\n<p><code>container.RegisterType&lt;TInterface, TType&gt;(new InjectionFactory(...));<\/code><\/p>\n<p><a href=\"https:\/\/github.com\/ikriv\/UnityDecoratorBug\">Github project ikriv\/UnityDecoratorBug<\/a> explores the behavior for different versions. Strictly speaking, the registration is self-contradictory: it may mean either<\/p>\n<p><code><span style=\"color: green;\">\/\/ (1) Resolve TInterface to whatever is returned by the InjectionFactory<\/span><br \/>\ncontainer.RegisterType&lt;TInterface&gt;(new InjectionFactory(...));<\/code><\/p>\n<p>or<\/p>\n<p><code><span style=\"color: green;\">\/\/ (2) Resolve TInterface to container-built instance of TType<\/span><br \/>\ncontainer.RegisterType&lt;TInterface, TType&gt;();<\/code><\/p>\n<p>Early versions of Unity ignored the TType part and used interpretation #1. In the <a href=\"https:\/\/github.com\/unitycontainer\/unity\/releases\/tag\/5.2.1.311\">release notes for version 5.2.1<\/a> it was declared that such registrations are no longer allowed, but in reality they were silently interpreted as (2), ignoring the InjectionFactory part.<\/p>\n<p>This led to a catastrophic problem in our code. We had a decorator type similar to this:<\/p>\n<pre>public class Decorator : IService\r\n{\r\n    public Decorator(IService service) {...}\r\n}<\/pre>\n<p>and the registration declaration was<\/p>\n<p><code>container.RegisterType&lt;IService, Decorator&gt;(<br \/>\nnew InjectionFactory(c =&gt; new Decorator(c.Resolve&lt;ConcreteService&gt;())));<\/code><\/p>\n<p>If the injection factory is ignored, this leads to an infinite recursion when resolving <code>IService<\/code>: <code>IService<\/code>\u00a0is resolved to\u00a0<code>Decorator<\/code>, that accepts\u00a0<code>IService<\/code> as a parameter, that is resolved to\u00a0<code>Decorator<\/code>, that needs <code>IService<\/code> as a paramter, that is resolved to <code>Decorator<\/code>&#8230;<\/p>\n<p>In Unity version 5.7.0 this was fixed and such registrations throw an <code>InvalidOperationException<\/code>.<\/p>\n<p>To recap:<\/p>\n<table>\n<tbody>\n<tr>\n<th>Unity version<\/th>\n<th>Effect of the registration<\/th>\n<\/tr>\n<tr>\n<td>&lt;=5.2.0<\/td>\n<td>Injection factory used<\/td>\n<\/tr>\n<tr>\n<td>5.2.1 to 5.6.1<\/td>\n<td>Injection factory ignored, StackOverflowException for decorators<\/td>\n<\/tr>\n<tr>\n<td>&gt;=5.7.0<\/td>\n<td>InvalidOperationException<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Lessons learnt:<\/p>\n<ul>\n<li>If something is not allowed, it must throw an exception<\/li>\n<li>Silently changing interpretation of existing code is evil.<\/li>\n<\/ul>\n<p>PS. This is probably the first time when <a href=\"https:\/\/stackoverflow.com\/\">stackoverflow.com<\/a> actually lived to its name: usually I search for other kinds of exceptions there. \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>After upgrading to a newer version of Unity, we found that our process began to fail with StackOverflowException. Investigation showed that it happened because the following registration behaves differently depending <a href=\"https:\/\/ikriv.com\/blog\/?p=2539\" 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":[1],"tags":[],"class_list":["entry","author-ikriv","post-2539","post","type-post","status-publish","format-standard","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2539","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=2539"}],"version-history":[{"count":2,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2539\/revisions"}],"predecessor-version":[{"id":2541,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2539\/revisions\/2541"}],"wp:attachment":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2539"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2539"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2539"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}