
{"id":1006,"date":"2012-05-24T15:10:42","date_gmt":"2012-05-24T19:10:42","guid":{"rendered":"http:\/\/www.ikriv.com\/blog\/?p=1006"},"modified":"2012-05-24T15:10:42","modified_gmt":"2012-05-24T19:10:42","slug":"nested-unity-containers-and-singletons","status":"publish","type":"post","link":"https:\/\/ikriv.com\/blog\/?p=1006","title":{"rendered":"Nested Unity Containers and Singletons"},"content":{"rendered":"<p>I work on a project that uses Unity dependency injection framework. You get objects from container by calling <code>Container.Resolve&lt;SomeType&gt;()<\/code>. By default each call to <code>Resolve()<\/code> will return a new object. You can switch to singleton mode by calling <code>Container.RegisterType&lt;SomeType&gt;(new ContainerControlledLifetimeManager())<\/code>. The singleton, if created, will be disposed when the container itself is disposed.<\/p>\n<p>Lately I started playing with nested containers. My application is large and composable, and nested containers look like a nice way to organize scope\/modularity. A particular part of the application can introduce objects in a nested container that the rest of the world does not know\/care about.<\/p>\n<p>I discovered that presence nested containers and singletons creates a number of permutations, and some permutations lead to weird and probably unwanted results. I wrote some tests, and here&#8217;s the summary of the most interesting discoveries.<\/p>\n<p>1. If type A is configured as singleton in parent container, and as default in child container, <code>Resolve&lt;A&gt;()<\/code> call on either container will return a parent-level singleton with lifetimed linked to the parent.<\/p>\n<p><code><\/p>\n<pre>            Container.RegisterType&lt;A&gt;(new ContainerControlledLifetimeManager());\n            var a1 = ChildContainer.Resolve&lt;A&gt;();\n            var a2 = Container.Resolve&lt;A&gt;();\n\n            Assert.AreSame(a1, a2);<\/pre>\n<p><\/code><\/p>\n<p>2. If type A is configured as singleton on both containers, two separate singleton instances will be created, each bound to its container lifetime.<\/p>\n<p><code><\/p>\n<pre>            Container.RegisterType&lt;A&gt;(new ContainerControlledLifetimeManager());\n            ChildContainer.RegisterType&lt;A&gt;(new ContainerControlledLifetimeManager());\n\n            var a11 = ChildContainer.Resolve&lt;A&gt;();\n            var a12 = ChildContainer.Resolve&lt;A&gt;();\n            var a21 = Container.Resolve&lt;A&gt;();\n            var a22 = Container.Resolve&lt;A&gt;();\n\n            Assert.AreSame(a11, a12);\n            Assert.AreSame(a21, a22);\n            Assert.AreNotSame(a11, a21);<\/pre>\n<p><\/code><\/p>\n<p>3. Here&#8217;s the most spectacular and weird one. Let&#8217;s say type B depends on A. Type A is configured as singleton in both containers. Type B is configured as singleton in parent container, but as default in child container.<\/p>\n<p>Under these circumstances the configuration of the object depends on the container through which <b>first<\/b> invocation of <code>Resolve&lt;B&gt;()<\/code> was made. All subsequent invocations through any container will return the originally created object.<\/p>\n<p>If the first container is parent, everything works fine. But if the fisrt container is child, things get icky. The object B is installed as a parent-level singleton, as per rule #1. However, its dependency of type A is installed as a child-level singleton with child-bound lifetime as per rule #2. Even if the parent container already contains an object of type A, it will be ignored. When child container is disposed, the main object B will stay intact, but its dependency A will be gone.<\/p>\n<p>And it all depends on the location of <b>first<\/b> instantiation, which may be not so easy to figure out, and it may change unexpectedly. Hello, bugs!<\/p>\n<p><code><\/p>\n<pre>            Container.RegisterType&lt;A&gt;(new ContainerControlledLifetimeManager());\n            Container.RegisterType&lt;B&gt;(new ContainerControlledLifetimeManager());\n            ChildContainer.RegisterType&lt;A&gt;(new ContainerControlledLifetimeManager());\n\n            var a1 = ChildContainer.Resolve&lt;A&gt;();\n            var a2 = Container.Resolve&lt;A&gt;();\n\n            var b1 = ChildContainer.Resolve&lt;B&gt;();\n            var b2 = Container.Resolve&lt;B&gt;();\n\n            Assert.AreSame(b2.A, a1); \/*!*\/<\/pre>\n<p><\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I work on a project that uses Unity dependency injection framework. You get objects from container by calling Container.Resolve&lt;SomeType&gt;(). By default each call to Resolve() will return a new object. <a href=\"https:\/\/ikriv.com\/blog\/?p=1006\" 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,4,5],"tags":[],"class_list":["entry","author-ikriv","post-1006","post","type-post","status-publish","format-standard","category-dotnet","category-hack","category-dev"],"_links":{"self":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1006","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=1006"}],"version-history":[{"count":0,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1006\/revisions"}],"wp:attachment":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1006"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1006"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1006"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}