views:

820

answers:

3

I'm using C# with Microsoft's Unity framework. I'm not quite sure how to solve this problem. It probably has something to do with my lack of understanding DI with Unity.

My problem can be summed up using the following example code:

class Train(Person p) { ... }

class Bus(Person p) { ... }

class Person(string name) { ... }

Person dad = new Person("joe");
Person son = new Person("timmy");

When I call the resolve method on Bus how can I be sure that the Person 'son' with the name 'timmy' is injected and when resolving Train how can I be sure that Person 'dad' with then name 'joe' is resolved?

I'm thinking maybe use named instances? But I'm at a loss. Any help would be appreciated.

As an aside, I would rather not create an IPerson interface.

+2  A: 

One way to solve this would be to use an injection constructor with a named registration.

// Register timmy this way  
Person son = new Person("Timmy");  
container.RegisterInstance<Person>("son", son);  

// OR register timmy this way  
container.RegisterType<Person>("son", new InjectionConstructor("Timmy"));  

// Either way, register bus this way.  
container.RegisterType<Bus>(new InjectionConstructor(container.Resolve<Person>("son")));  

// Repeat for Joe / Train
Daniel Auger
+6  A: 

Unless you register respectively "joe" and "timmy" as named dependencies, you can't be sure that "timmy" is injected into Schoolbus. In fact, if you attempt to register two instances of the same class as unnamed dependencies, you will have an ambiguous setup, and you will not be able to resolve Person at all.

In general, if you have to register a lot of named instances you are probably going about DI in the wrong way. The main idea of DI is to resolve Domain Services more than Domain Objects.

The primary idea of DI is to provide a mechanism that allows you to resolve abstract types (interfaces or abstract classes) into concrete types. Your example has no abstract types, so it doesn't really make a lot of sense.

Mark Seemann
Thanks for your insightful response. Although you may be right, DI and IoC are new concepts to me so I'm still trying to figure out the best design.
JP
That's fair enough - you may want to read my book, then :)
Mark Seemann
JP, do check out his book. I've found it to be very insightful.
Daniel Auger
Awesome... it sounds intriguing. I just started reading the intro chapter.
JP
+2  A: 

Mark Seeman got it right. And I sympathize with your confusion. I went through it myself when I learned to use automatic dependency injection containers. The problem is that there are many valid and reasonable ways to design and use objects. Yet only some of those approaches work with automatic dependency injectorion containers.

My personal history: I learned OO principles of object construction and Inversion Of Control long before I learned how to use Inversion of Control containers like the Unity or Castle Windsor containers. I acquired the habit of writing code like this:

public class Foo
{   IService _service;   int _accountNumber;
   public Foo(IService service, int accountNumber)
   {
      _service = service;
      _accountNumber = accountNumber;
   }
   public void SaveAccount()
   {
       _service.Save(_accountNumber);

   }
}
public class Program
{
     public static void Main()
     {
        Foo foo = new Foo(new Service(),1234);
        foo.Save();
     }
}

In this design, my Foo class is responsible for saving accounts to the database. It needs an account number to do that and a service to do the dirty work. This is somewhat similar to the concreted classes you provided above, where each object takes some unique values in the constructor. This works fine when you instantiate the objects with your own code. You can pass in the appropriate values at the right time.

However, when I learned about automatic dependency injection containers, I found that I was no longer instantiating Foo by hand. The container would instantiate the constructor arguments for me. This was a great convenience for the services like IService. But it obviously does not work so well for integers and strings and the like. In those cases, it would provide a default value (like zero for an integer) . Instead, I had been accustomed to passing in context-specific values like account number, name, etc... So I had to adjust my style of coding and design to be like this:

public class Foo
{
   IService _service;
   public Foo(IService service)
   {
      _service = service;
   }
   public void SaveAccount(int accountNumber)
   {
       _service.Save_accountNumber);

   }
}
public class Program
{
     public static void Main()
     {
        Foo foo = new Foo(new Service(),1234);
        foo.Save();
     }
}

It appears that both Foo classes are valid designs. But the second is useable with automatic dependency injection, and the first is not.

David Allen
Shouldn't the 2nd Main() method be more like: Foo foo = new Foo(new Service());foo.Save(1234); ?
McBainUK