Share via


Annotating Objects for Method Call Injection

Unity supports dependency injection to set the values of parameters of methods specified through attributes applied to members of the target class. You can use the Unity container to generate instances of dependent objects and wire up the target class method parameters with these instances. This topic explains how to use an attribute that is applied to one or more method declarations of a class to define the dependency injection requirements of that class.

To perform injection of dependent classes into objects you create through the Unity container, you apply the InjectionMethod attribute to the method declarations of a class. The Unity container will force the target object (the object you specify in a Resolve method call) to create an instance of the dependent class and then call the target method. If required, your code in the method can save this instance by assigning it to a class-level variable.

Method call injection is a form of optional injection of dependent objects that you can use if you use the Unity container to generate the target object. Unity instantiates dependent objects defined in parameters of methods that carry the InjectionMethod attribute within the scope of the target object. Then it calls the attributed method of the target object before returning the object to the caller. You must apply the InjectionMethod attribute in the target class to initiate method call injection. For more information, see Notes on Using Method Call Injection.

To use method call injection to create dependent objects for a class

  1. Define a method in the target class and apply the InjectionMethod attribute to it to indicate that any types defined in parameters of the method are dependencies of the class. The following code demonstrates the most common scenario—saving the dependent object instance in a class-level variable—for a class named MyObject that exposes a method named Initialize that takes as a parameter a reference to an instance of another class named SomeOtherObject (not defined in this code).

    public class MyObject
    {
    
      private SomeOtherObject dependentObject;
    
      [InjectionMethod]
      public void Initialize(SomeOtherObject dep) 
      {
        // assign the dependent object to a class-level variable
        dependentObject = dep;
      }
    } 
    
    'Usage
    Public Class MyObject
    
      Private dependentObject As SomeOtherObject
    
      <InjectionMethod()> _
      Public Sub Initialize(dep As SomeOtherObject)
        ' assign the dependent object to a class-level variable
        dependentObject = dep
      End Sub
    
    End Class 
    
  2. In your run-time code, use the Resolve method of the container to create an instance of the target class. The Unity container will instantiate the dependent concrete class defined in the attributed method, inject it into the target class, and execute the method. For example, the following code shows how you can instantiate the example target class named MyObject containing an attributed method that has a dependency on a class named SomeOtherObject.

    IUnityContainer uContainer = new UnityContainer();
    MyObject myInstance = uContainer.Resolve<MyObject>();
    
    'Usage
    Dim uContainer As IUnityContainer = New UnityContainer()
    Dim myInstance As MyObject = uContainer.Resolve(Of MyObject)()
    
  3. In addition to using concrete types for the dependencies in target object methods, you can use interfaces or base class types and then register mappings in the Unity container to translate these types into the appropriate concrete types. Define a method in the target class that takes as parameters interfaces or base types. For example, the following code shows a target class named MyObject containing a method named Initialize that takes as parameters an object named interfaceObj that implements the interface named IMyInterface and an object named baseObj that inherits from the class MyBaseClass.

    public class MyObject
    {
    
      private IMyInterface depObjectA;
      private MyBaseClass depObjectB;
    
      [InjectionMethod]
      public void Initialize(IMyInterface interfaceObj, MyBaseClass baseObj) 
      {
        depObjectA = interfaceObj;
        depObjectB = baseObj;
      }
    } 
    
    'Usage
    Public Class MyObject
    
      Private depObjectA As IMyInterface
      Private depObjectB As MyBaseClass
    
      <InjectionMethod()> _
      Public Sub Initialize(interfaceObj As IMyInterface, baseObj As MyBaseClass)
        depObjectA = interfaceObj
        depObjectB = baseObj
      End Sub
    
    End Class 
    
  4. In your run-time code, register the mappings you require for the interface and base class types, and then use the Resolve method of the container to create an instance of the target class. The Unity container will instantiate an instance of each of the mapped concrete types for the dependent classes, and inject them into the target class. For example, the following code shows how you can instantiate the example target class named MyObject containing an attributed method that has dependencies on the two classes, FirstObject and SecondObject.

    IUnityContainer uContainer = new UnityContainer()
       .RegisterType<IMyInterface, FirstObject>()
       .RegisterType<MyBaseClass, SecondObject>();
    MyObject myInstance = uContainer.Resolve<MyObject>();
    
    'Usage
    Dim uContainer As IUnityContainer = New UnityContainer() _
       .RegisterType(Of IMyInterface, FirstObject)() _
       .RegisterType(Of MyBaseClass, SecondObject)()
    Dim myInstance As MyObject = uContainer.Resolve(Of MyObject)()
    

Specifying Named Type Mappings

The preceding example shows how you can resolve types for method parameters using the default (unnamed) mappings in the container. If you register more than one mapping for a type, you must differentiate them by using a name. In this case, you can specify which named mapping the container will use to resolve the method parameter types.

To use attributed method call injection with named container type mappings

  1. Define a method in the target class that takes as a parameter the concrete type of the dependent class, and apply a Dependency attribute to the parameter that specifies the name of the registered mapping to use. For example, the following code shows a target class named MyObject containing a method named Initialize that has a dependency on a service that implements the IMyService interface. The code assumes that the container contains a mapping defined with the name DataService between the IMyService interface and a concrete implementation of that interface.

    public class MyObject
    {
      private IMyService myDataService;
    
      [InjectionMethod]
      public void Initialize([Dependency("DataService")] IMyService theService) 
      {
        // assign the dependent object to a class-level variable
        myDataService = theService;
      }
    } 
    
    'Usage
    Public Class MyObject
    
      Private myDataService As IMyService
    
      <InjectionMethod()> _
      Public Sub Initialize(<Dependency("DataService")> theService As IMyService)
        ' assign the dependent object to a class-level variable
        myDataService = theService
      End Sub
    
    End Class 
    
  2. In your run-time code, use the Resolve method of the container to create an instance of the target class. The Unity container will instantiate the dependent concrete class defined in the attributed method, inject it into the target class, and execute the method. For example, the following code shows how you can instantiate the example class shown above.

    IUnityContainer uContainer = new UnityContainer();
    MyObject myInstance = uContainer.Resolve<MyObject>();
    
    'Usage
    Dim uContainer As IUnityContainer = New UnityContainer()
    Dim myInstance As MyObject = uContainer.Resolve(Of MyObject)()
    

Note

If you specify a named mapping and there is no mapping registered for that type and name, the container will raise an exception.

Notes on Using Method Call Injection

The following notes will help you to get the greatest benefit from using method call injection with Unity.

Method Call Injection with Existing Objects

If you use configuration or the RegisterInstance method to register an existing object, method call injection does not take place on that object because it has already been created outside of the influence of the Unity container. However, you can call the BuildUp method of the container and pass it the existing object to force method call injection to take place on that object.

Avoiding Circular References

Dependency injection mechanisms can cause application errors if there are circular references between objects that the container will create. For more details, see Circular References with Dependency Injection.

When to Use Method Call Injection

You should consider using method call injection in the following situations:

  • When you want to instantiate dependent objects automatically when you instantiate the parent object.
  • When you want a simple approach that makes it easy to see in the code what the dependencies are for each class.
  • When you want to avoid exposing a public property just to implement injection, and instead use an Initialize method.
  • When the parent object requires a large number of constructors that forward to each other, making debugging and maintenance difficult.
  • When the parent object constructors require a large number of parameters, especially if they are of similar types and the only way to identify them is by position.
  • When you want to hide the dependent objects by not exposing them as properties.
  • When you want to control which objects are injected by editing the code of the dependent object instead of the parent object or application.

If you are not sure which type of injection to use, the recommendation is that you use constructor injection. This is likely to satisfy almost all general requirements, unless you need to perform injection on existing object instances.

Note

You can also configure method call injection at design time or run time. For more information, see Configuring Unity.