3

I am using .net unity dependency injection framework within an application.

This project uses some 'third party libraries' (libraries coded by other teams in same company)

I register some 'Car' classes of my third party library this way:

 unityContainer.RegisterType<ICar,Car>();

Now, people who develop the 'Car' class decide that it would be better if the constructor took a boolean inside the constructor.

When I get the new version of their library, my code breaks at runtime (I cannot detect the error in the build pipeline):

Resolving parameter \"hasRadio\" of constructor Car Resolving System.Boolean,(none)\r\n","exceptionType":"Microsoft.Practices.Unity.ResolutionFailedException"...

So my question is, how would you manage to avoid such issues ?

Is it a matter of best practices to introduce rules such as 'never use primitives in constructors' ?

Is it my test coverage that is not good enough ? For example, should I check that all classes of my unity dependency graph are instantiated with an InjectionConstructor when class constructor contains some primitive ? (I am not even sure one can do it with UnityContainer, whose Registrations field does not give back the InjectionConstructors)

Should I always instantiate classes from third party libraries explicitely (with InjectionFactory), to make sure that build pipeline fails when other team changes constructors without changing my code ? (I am not really happy with this option as it kind'of violates dependency injection principle : /)

David
  • 1,138
  • 5
  • 15
  • Yes, your unit test coverage is not good enough - you should have unit test that checks if types can be instantiated the way production code does it (in your case via Unity) and hence change that is taking version of new library should be rejected at build time... How you going to resolve the fact other library is not convenient for DI container of your choice is up to you... (You obviously seen http://stackoverflow.com/questions/787001/can-i-pass-constructor-parameters-to-unitys-resolve-method to pass constructor args...) – Alexei Levenkov Oct 25 '16 at 17:15

1 Answers1

4

Is it a matter of best practices to introduce rules such as 'never use primitives in constructors' ?

No. Primitive dependencies can be used like any other dependencies. Many classes need such dependencies to work. For example, a class that communicates with an SMTP server needs the address of the server and a port.

Is it my test coverage that is not good enough?

You could add more tests to verify that the object graph can actually be created, I am not sure how easy would that be. However, if you use Pure DI, you can detect such problems at compile time.

Should I always instantiate classes from third party libraries explicitely (with InjectionFactory), to make sure that build pipeline fails when other team changes constructors without changing my code ?

I don't think there is a rule here. You can do that if you want. But if you use Pure DI as I mentioned above, your problem is already solved.

I am not really happy with this option as it kind'of violates dependency injection principle

I don't think this violates the Dependency Inversion Principle. However, I think that you violate DIP in a different way:

If you apply the Dependency Inversion Principle correctly, then you should not use the ICar abstraction from your application classes directly since it belongs to the 3rd party component, not you application.

You should define abstractions inside your application (in the terms of your application domain), and then create adaptors to implements such abstractions that use the 3rd party API. For example:

namespace MyApplication
{
    public interface IMyCar
    {
        void DoSomething();
    }
}

namespace Adaptors
{
    public class CarAdaptor : MyApplication.IMyCar
    {
        private readonly ThirdParty.ICar car;

        public CarAdaptor(ThirdParty.ICar car) {this.car = car;}

        public void DoSomething() { car.Do3rdPartyThing();}
    }
}

Another thing to note here is that from the name of the class Car, this might be a newable, not an injectable. You want to inject injectables only. See this article for more details.

Yacoub Massad
  • 27,509
  • 2
  • 36
  • 62