Constructor Injection in the CAB (Introduction to the CAB/SCSF Part 6)

Introduction

Part 3 of this series of articles described the different types of dependency injection that we can use in general. These are setter injection, constructor injection and interface injection.

Part 5 showed how dependency injection works in the CAB. The examples given there exclusively used setter injection. In general we don’t use interface injection in CAB projects, but we can use constructor injection. This short article will show you how.

Constructor Injection Example

As is fairly obvious, constructor injection injects your dependencies in a constructor rather than a setter. To do this in the CAB we once again use attributes.

We can return to our original example of setter injection and rework it to use constructor injection. To do this we add a constructor to our Component2 class as below, and we remove the setters we were using previously (they are commented out below):

    public class Component2
    {
        private Component1 component11;
        //[ComponentDependency("FirstComponent1")]
        //public Component1 Component11
        //{
        //    set { component11 = value; }
        //}
 
        private Component1 component12;
        //[CreateNew]
        //public Component1 Component12
        //{
        //    set { component12 = value; }
        //}
 
        [InjectionConstructor]
        public Component2([ComponentDependency("FirstComponent1")]Component1 component, [CreateNew]Component1 component2)
        {
            component11 = component;
            component12 = component2;
        }
    }
 

The constructor will inject the same dependencies as the setters did previously if we create Component2 with the AddNew method of a WorkItem’s Items collection as below:

RootWorkItem.Items.AddNew<Component2>("Component2");

When the CAB creates a component with AddNew it looks for a constructor decorated with the InjectionConstructor attribute. If it finds one it calls that constructor, and injects dependencies via the constructor parameters as shown. It applies the usual rules if any of the constructor parameters are decorated with the CreateNew, ServiceDependency or ComponentDependency attributes.

The code for this example is available.

Constructor Injection Issues

Note that if there’s only one constructor in the class you don’t actually need the InjectionConstructor attribute. The CAB assumes you mean to use that one constructor as the injection constructor. Our example above will work without the attribute. If you have more than one constructor in the class you do need the attribute, and in general it’s probably good practice to include the attribute if you are using constructor injection.

Note also that in the original example in part 5 we created the Component2 instance with a standard ‘new’ keyword and then added it to the items collection:

            Component2 component2 = new Component2();
            RootWorkItem.Items.Add(component2);

If we do this with our new Component2 the injection will not work. In fact the code won’t even compile as we no longer have a constructor that doesn’t take any arguments. Even if we put a constructor with no arguments into Component2 we’re clearly not going to get any dependency injection as we are not creating the component with the CAB and the standard ‘new’ keyword doesn’t know how to do constructor injection.

Finally it’s worth noting that if you don’t put any attribute on an argument of an injection constructor the CAB treats it as if it had a ServiceDependency attribute. That is, it tries to inject a service of the appropriate type. If there’s no such service it creates a new one and adds it to the Services collection. This is NOT the behaviour you get with the ServiceDependency attribute: if you apply the ServiceDependency attribute and there’s no such service you get a ServiceMissingException when you try to create the new object.

Setter Injection vs Constructor Injection

Whether you use setter injection or constructor injection is really a personal choice. There isn’t much to choose between them in terms of functionality. As we’ve seen if you create your object with the ‘new’ keyword initially and then add it to a CAB collection you can’t use constructor injection. Also arguably the syntax for the constructors is a little cumbersome. But normally your CAB objects will be created and added to CAB collections with the AddNew keyword or something equivalent. In this case both setter injection and constructor injection inject the dependencies on creation of the object, and the two types of injection are functionally equivalent.

Conclusion

We’ve now covered dependency injection in the CAB in some detail. Part 7 of this series will discuss services in the CAB.

5 thoughts on “Constructor Injection in the CAB (Introduction to the CAB/SCSF Part 6)

  1. I think the choice between Setter and Construction injection is substantial. For example, if you decide to new an object and then add it to one of the WorkItem collection, you have bypassed the Constructor injections completely because the builder ignores them (there is nothing to construct). On the other hand, the builder will set all properties marked for setter injection even for an object added to the collection.

    Inheritance is a problem with constructor injection in CAB. If B inherits from A, and both A and B have injection constructors, CAB will perform injection for the derived class (B) but not for the base class (A). That means when I write (B), I have to be aware that it’s base constructor’s parms are injected and mark (B)’s constructor parms with the same attributes. I am unwilling to give (B) that degree of awareness of (A). Consequently, I have been unable to use constructor injection for any class that is not final; it almost obliges me to seal a class that uses constructor injection.

  2. I also have a beef with setter injection. In CAB, every injection member (constructor, property, method) must be public. There are good “code access security” reasons for this requirement that are out of scope for this comment.

    In the case of an injected property, the “set” must be public. The “get” does not have to be public and, in general, I don’t want the property to be public at all. As a result, injected properties are often designed with a public set and a private/protected get. You’ll see this in SCSF code (take a look at the Presenter property in the view of the SCSF MVP recipe).

    That violates one of the Framework Design principles: thou shalt not have a setter with wider access than the getter. INMHO, that principle is not arbitrary but, rather, the fruit of real experience.

    The other thing I don’t like about property injection is that one ends up with a bunch of marked properties scattered about the class. You can count on the builder calling them at runtime before anything else has a chance to use your class instance. But during test (unless you incorporate the builder in your test harness), the test developer must remember to set every property before using the object and do so in an the order that CAB will set them. The tester should not have the burden of that knowledge.

    Therefore, I prefer the method injection option. You get one of these when you decorate a method with the InjectionMethodAttribute as in this example:
    <![CDATA[
    /// Object Builder injected presenter – do not call.
    [InjectionMethod]
    public void InjectPresenter([CreateNew] MyPresenter presenter)
    {
    //.. do something
    }
    ]]>

    Notice how you decorate the parameter with an attribute just as you would the parameters of a constructor.

    The virtues of this approach are (a) all injection in one place making the class easier to test, (b) distinctive name makes it easy to find, (c) conforms to official Framework Design Guidelines, (d) unlike constructor injection, it will always be called by object builder.

Leave a comment