Is it better to create singleton access to the unity container or pass it through the application?

I plunge into using the IoC framework, and I decided to use Unity. One of the things that I still do not fully understand is how to resolve objects deeper in the application. I suspect that I simply did not have a light bulb at the moment that would be clear.

So, I'm trying to do something like the following in psuedo'ish code

void Workflow(IUnityContatiner contatiner, XPathNavigator someXml) { testSuiteParser = container.Resolve<ITestSuiteParser> TestSuite testSuite = testSuiteParser.Parse(SomeXml) // Do some mind blowing stuff here } 

So testSuiteParser.Parse does the following

 TestSuite Parse(XPathNavigator someXml) { TestStuite testSuite = ??? // I want to get this from my Unity Container List<XPathNavigator> aListOfNodes = DoSomeThingToGetNodes(someXml) foreach (XPathNavigator blah in aListOfNodes) { //EDIT I want to get this from my Unity Container TestCase testCase = new TestCase() testSuite.TestCase.Add(testCase); } } 

I see three options:

  • Create a Singleton to save a unity container with which I can access anywhere. I'm really not a fan of this approach. Adding such a dependency to use the dependency injection infrastructure seems a bit on the odd side.
  • Pass the IUnityContainer to my TestSuiteParser class and each of its children (suppose these are n levels deep or actually about 3 levels). Passing the IUnityContainer everywhere just looks weird. I just need to overcome this.
  • Have a light bulb moment on the right way to use Unity. Hoping someone can help flip the switch.

[EDIT] One of the things that I didn't understand about is that I want to create a new instance of the test case for each iteration of the foreach statement. In the above example, you need to analyze the test suite configuration and fill out the collection of test case objects

+45
design c # ioc-container unity-container
Mar 05 '10 at 12:01
source share
4 answers

The right approach to DI is to use Constructor Injection or another DI template (but the most typical is constructor introduction) to embed dependencies to the consumer, regardless of the DI container .

In your example, it looks like you need TestSuite and TestCase dependencies, so your TestSuiteParser class should statically declare that it requires these dependencies, asking them through the (only) constructor:

 public class TestSuiteParser { private readonly TestSuite testSuite; private readonly TestCase testCase; public TestSuiteParser(TestSuite testSuite, TestCase testCase) { if(testSuite == null) { throw new ArgumentNullException(testSuite); } if(testCase == null) { throw new ArgumentNullException(testCase); } this.testSuite = testSuite; this.testCase = testCase; } // ... } 

Notice how the combination of the readonly keyword and the Guard block protects class invariants, ensuring that dependencies are available to any successfully created TestSuiteParser instance.

Now you can implement the Parse method as follows:

 public TestSuite Parse(XPathNavigator someXml) { List<XPathNavigator> aListOfNodes = DoSomeThingToGetNodes(someXml) foreach (XPathNavigator blah in aListOfNodes) { this.testSuite.TestCase.Add(this.testCase); } } 

(however, I suspect that more than one TestCase may be involved, in which case you can add an abstract Factory instead of a single TestCase.)

From Composition Root you can configure Unity (or any other container):

 container.RegisterType<TestSuite, ConcreteTestSuite>(); container.RegisterType<TestCase, ConcreteTestCase>(); container.RegisterType<TestSuiteParser>(); var parser = container.Resolve<TestSuiteParser>(); 

When the container allows TestSuiteParser, it understands the constructor installation pattern, so it is an Auto-Wires instance with all the necessary dependencies.

Creating a Singleton container or transferring a container are just two variations of the Locator antivirus program , so I would not recommend that.

+48
Mar 05 '10 at 12:27
source share

I am new to Dependency Injection and I also had this question. I struggled to deal with DI, mainly because I focused on applying DI to only one class that I was working on, and as soon as I added dependencies to the constructor, I immediately tried to find a way to achieve container unity in those places where this class needs to be created so that I can call the Resolve method in the class. As a result, I was thinking about how to make the unity container globally accessible as static, or wrap it in a singleton class.

I read the answers here and did not quite understand what was being explained. Which finally helped me “get this” in this article:

http://www.devtrends.co.uk/blog/how-not-to-do-dependency-injection-the-static-or-singleton-container

And this item, in particular, was the moment of "light bulb":

"99% of your code base should not be known by your IoC container. Only the root class or bootable media uses the container, and even then one single permission call is all that is usually needed to build a dependency graph and run an application or request.

This article helped me understand that, in fact, I should not have access to the unity container throughout the application, but only at the root of the application. Therefore, I have to reapply the DI principle on the way back to the root class of the application.

Hope this helps others who are just as confused as I am! :)

+12
Dec 17
source share

You really do not need to use your container directly in many places of your application. You should take all your dependencies in the constructor and not get to them from your methods. You could look something like this:

 public class TestSuiteParser : ITestSuiteParser { private TestSuite testSuite; public TestSuitParser(TestSuit testSuite) { this.testSuite = testSuite; } TestSuite Parse(XPathNavigator someXml) { List<XPathNavigator> aListOfNodes = DoSomeThingToGetNodes(someXml) foreach (XPathNavigator blah in aListOfNodes) { //I don't understand what you are trying to do here? TestCase testCase = ??? // I want to get this from my Unity Container testSuite.TestCase.Add(testCase); } } } 

And then you do it the same way throughout the application. You, of course, have to decide something at some point. For example, in asp.net mvc this place is in the factory controller. This is the factory that creates the controller. In this factory, you will use your container to resolve the parameters of your controller. But this is just one place in the whole application (maybe a few more places when you do more advanced things).

There is also a good project called CommonServiceLocator . This is a project that has a common interface for all popular ioc containers, so you have no dependency on a specific container.

+4
Mar 05 '10 at 12:22
source share

If only one can have a “ServiceLocator”, which is passed around the service constructors, but somehow “Declares” the alleged dependencies of the class into which it is introduced (that is, it does not hide the dependencies) ... that way, everything (?) objections to the service locator pattern can be deferred.

 public class MyBusinessClass { public MyBusinessClass(IServiceResolver<Dependency1, Dependency2, Dependency3> locator) { //keep the resolver for later use } } 

Unfortunately, the above will obviously ever exist in a dream, since C # forbids variable common parameters (still), so manually adding a new common interface every time you need an additional common parameter will be cumbersome.

If, on the other hand, the above can be achieved despite the C # restriction as follows ...

 public class MyBusinessClass { public MyBusinessClass(IServiceResolver<TArg<Dependency1, TArg<Dependency2, TArg<Dependency3>>> locator) { //keep the resolver for later use } } 

Thus, to achieve the same thing you only need to do additional typing. I'm still not sure that if, given the proper design of the TArg class (I assume that smart inheritance will be used to ensure that TArg endlessly TArg common parameters), DI containers will be able to resolve IServiceResolver correctly. The idea, ultimately, is simply to bypass the same implementation of IServiceResolver no matter what general declaration is found in the constructor of the class into which it is entered.

0
Nov 30 '16 at 12:04
source share



All Articles