There are a variety of Dependency Injection patterns. Previously, we looked at the Service Locator pattern (or anti-pattern, depending on your point of view). We contrasted this with Constructor Injection to see the pros and cons of each approach.
In my presentation on Dependency Injection (and also my Pluralsight course), we cover Constructor Injection and Property Injection, and we see how Property Injection can be a good choice if we have a default value that we want to use at runtime, but we still want to be able to inject a different object for testing. But there are other ways to implement the Property Injection pattern.
Let's take a look at these approaches and see how they compare to Constructor Injection.
Property Injection with a Good Default
In the sample code, we look at using Property Injection as a way to facilitate unit testing. In this case, we're using it with a service. Here's that property code:
Our property is of type IPersonService -- this is the interface that specifies the service. When we run our application, we want to use a real WCF service (which is called "PersonServiceClient" -- technically, this is a proxy that points to the production service, but the proxy pattern lets us treat this as if it were the service itself). Because of the way we have this property set up, our repository will use our production WCF service by default (i.e., if we do nothing).
So, if we call a method such as "ServiceProxy.GetPeople()", it will run the getter on the ServiceProxy property. The first time through, the backing field ("_serviceProxy") will be null. If it is null, then we new up an instance of our production WCF service ("PersonServiceClient") and assign it to the backing field. Then we return that value.
If we call "ServiceProxy.GetPeople()" a second time, the backing field will not be null (it will be already populated with our production service), and it will simply use the service proxy we already created.
And this is great at runtime. When we run our application, we want to use the production WCF service 100% of the time. But we are also leaving ourselves open to injecting that service through the property.
The issue is when we get to unit testing. We want to be able to test our repository class without needing to rely on a production WCF service. If the production service is down for some reason, then our tests will fail. But we don't want to test the service here; we want to test the repository class.
Because of the way we implemented our property, we can use Property Injection to inject a mock service into our tests. Here's what one of our tests looks like:
This creates a mock object (using Moq) that will behave the way we want it to for testing purposes.
Using Property Injection this way works really well when we have a default value that we use at runtime, but we want to swap out that value for testing purposes.
Property Injection with No Default
There is another way to implement the Property Injection pattern: by using an automatic property. Here's what that property would look like in our repository:
This property still has a setter, which means that we can swap out the value. But this time, we do not have a default value for the property. This means that we need to set the property somehow. We can do this by configuring our Dependency Injection container.
Injection with Unity
One of our samples from the session uses the Unity container (from the Microsoft Patterns & Practices team). Here's how we would need to expand the container configuration to inject this property (this is in the composition root of our application where we configure our container):
The syntax for Unity is a little cryptic. What we're saying here is that we want to add some configuration when the container resolves a "PersonServiceRepository" (which is our repository type). When Unity resolves this type, we would also like it to inject a value for the "ServiceProxy" property.
For this, Unity will go through its standard process. It will look at the ServiceProxy property, determine its type ("IPersonService"), and figure out how to resolve an object of that type. Now, we will need to give Unity a little more information for this. Here's one way of doing that:
In this code, we create an instance of the "PersonServiceClient" (that points to our production WCF service) and then register that instance with the Unity container. So, when we ask Unity to resolve an "IPersonService", it will provide us with the "serviceProxy" instance that we created.
And to take this a step further, when Unity is asked to inject the "ServiceProxy" property (which is of type "IPersonService"), it will inject the "serviceProxy" instance.
Our unit tests could remain unchanged -- we can manually set the "ServiceProxy" property in our tests (as we did above). Or, if we want to use a Unity container in our tests, we could configure the Unity container to use the mock service rather than the production service.
Other DI containers (such as MEF) may use different methodology to inject the property. I've worked with Prism (an application framework that also comes from the Microsoft Patterns & Practices team), and it provides an abstraction that allows us to configure property injection by using an attribute.
In that case, we just mark our ServiceProxy property with a "[Dependency]" attribute, and the framework ensures that the container injects that property.
A Big Drawback
There is a big drawback to implementing Property Injection this way. I managed to find this out the hard way when I was new to dependency injection. But it does make sense when you think about it.
When we do not supply a default value for the property, what happens if we try to include the following code?
This is the constructor for our repository. In the constructor, we use the ServiceProxy property to populate some data. We may want to do this if we want to initialize some data when our object gets created.
The problem is that this will result in a runtime error. (But it compiles just fine.)
Let's walk through what happens when our repository gets created (we'll assume its being created by the Unity container).
1. The Unity container creates an instance of the PersonServiceRepository. (This executes the constructor.)
2. The Unity container injects the ServiceProxy property based on the "RegisterType" configuration that we saw above.
Do you see the problem now? Then constructor executes *before* the property is injected. (This is also the case when using the "[Dependency]" attribute with Prism.) This results in a null-reference exception when we try to access the ServiceProxy property in the constructor.
Mitigating the Risks
We've seen the potential risks with a "no default" implementation of Property Injection. How do we get around this?
Well, we'll go back to our recommendations from the Dependency Injection session: We should favor using Constructor Injection for items that our object requires but do not have good default values. So, instead of using Property Injection in this case, we can move the dependency into the constructor as a parameter.
So, our constructor would look like this:
This lets us safely use the "ServiceProxy" property in our constructor.
Another option is to move this initialization code out of the constructor. We generally want our constructors to run quickly, and this means avoiding network or database calls during construction. We can look at lazy loading our data when we actually use it. Then we can safely use Property Injection in our class.
After looking at this, we may think that Property Injection should be avoided. But Property Injection is still a good dependency injection pattern. We just need to be aware of the consequences of the pattern before we implement it in our code (this is true of all design patterns). And this leads us to the recommendations as we approach dependency injection.
Use Property Injection when we have a default value that we want to swap out for testing. But favor Constructor Injection as much as possible. Constructor Injection has the benefit of keeping dependencies obvious (as we saw when we looked at the Service Locator).
Dependency Injection helps us create loosely coupled code that facilitates maintainability, extensibility, and testability. The better we understand the pros and cons of each of the dependency injection patterns, the better the results will be in our applications.