Partager via


Annotating Objects for Property (Setter) Injection

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

The latest Unity Application Block information can be found at the Unity Application Block site.

The Unity Application Block supports dependency injection 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 with these instances.

Typical Goals

In this scenario, you use an attribute that is applied to one or more property declarations of a class to define the dependency injection requirements of that class. The attribute can specify parameters for the attribute to control its behavior, such as the name of a registered mapping.

Solution

To perform injection of dependent classes into objects you create through the Unity container, you apply attributes to the classes that specify these dependencies. For property (or setter) injection, you apply the Dependency attribute to the property declarations of a class. The Unity container will create an instance of the dependent class within the scope of the target object (the object you specify in a Resolve method call) and assign this dependent object to the attributed property of the target object.

Property injection is a form of optional injection of dependent objects, provided developers use the Unity container to generate the target object. The dependent object instance is generated before the container returns the target object. In addition, unlike constructor injection, you must apply the appropriate attribute in the target class to initiate property injection.

To use property (setter) injection to create dependent objects for a class

  1. Define a property in the target class and apply the Dependency attribute to it to indicate that the type defined and exposed by the property is a dependency of the class. The following code demonstrates property injection for a class named MyObject that exposes as a property a reference an instance of another class named SomeOtherObject (not defined in this code).

    public class MyObject
    {
      private SomeOtherObject _dependentObject;
    
      [Dependency]
      public SomeOtherObject DependentObject 
      {
        get { return _dependentObject; }
        set { _dependentObject = value; }
      }
    } 
    
    'Usage
    Public Class MyObject
      Private _dependentObject As SomeOtherObject
    
      <Dependency()> _
      Public Property DependentObject() As SomeOtherObject
        Get
          Return _dependentObject
        End Get
        Set(ByVal value As SomeOtherObject)
          _dependentObject = value
        End Set
      End Property
    
    End Class 
    
  2. In your run-time code, use the Resolve method of the container to create an instance of the target class, and then reference the property containing the dependent object. The Unity container will instantiate the dependent concrete class defined in the attributed property and inject it into the target class. For example, the following code shows how you can instantiate the example target class named MyObject containing an attributed property that has a dependency on a class named SomeOtherObject and then retrieve the dependent object from the DependentObject property.

    IUnityContainer uContainer = new UnityContainer();
    MyObject myInstance = uContainer.Resolve<MyObject>();
    
    // now access the property containing the dependency
    SomeOtherObject depObj = myInstance.DependentObject;
    
    'Usage
    Dim uContainer As IUnityContainer = New UnityContainer()
    Dim myInstance As MyObject = uContainer.Resolve(Of MyObject)()
    
    ' now access the property containing the dependency
    Dim depObj As SomeOtherObject = myInstance.DependentObject
    
  3. In addition to using concrete types for the dependencies in target object properties, you can use interfaces or base class types, and then register mappings in the Unity container to translate these types into the correct concrete types. Define a property in the target class as an interface or base type. For example, the following code shows a target class named MyObject containing properties named InterfaceObject and BaseObject that have dependencies on a class that implements the interface named IMyInterface and on a class that inherits from MyBaseClass.

    public class MyObject
    {
    
      private IMyInterface _interfaceObj;
      private MyBaseClass _baseObj;
    
      [Dependency]
      public IMyInterface InterfaceObject
      {
        get { return _interfaceObj; }
        set { _interfaceObj = value; }
      }
    
      [Dependency]
      public MyBaseClass BaseObject
      {
        get { return _baseObj; }
        set { _baseObj = value; }
      }
    
    } 
    
    'Usage
    Public Class MyObject
    
      Private _interfaceObj As IMyInterface
      Private _baseObj As MyBaseClass
    
      <Dependency()> _
      Public Property InterfaceObject() As IMyInterface
        Get
          Return _interfaceObj
        End Get
        Set(ByVal value As IMyInterface)
          _interfaceObj = value
        End Set
      End Property
    
      <Dependency()> _
      Public Property BaseObject() As MyBaseClass
        Get
          Return _baseObj
        End Get
        Set(ByVal value As MyBaseClass)
          _baseObj = value
        End Set
      End Property
    
    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 create 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 two properties that have dependencies on the two classes named FirstObject and SecondObject.

    IUnityContainer uContainer = new UnityContainer()
       .RegisterType<IMyInterface, FirstObject>()
       .RegisterType<MyBaseClass, SecondObject>();
    MyObject myInstance = uContainer.Resolve<MyObject>();
    
    // now access the properties containing the dependencies
    IMyInterface depObjA = myInstance.InterfaceObject;
    MyBaseClass depObjB = myInstance.BaseObject;
    
    'Usage
    Dim uContainer As IUnityContainer = New UnityContainer() _
       .RegisterType(Of IMyInterface, FirstObject)() _
       .RegisterType(Of MyBaseClass, SecondObject)()
    Dim myInstance As MyObject = uContainer.Resolve(Of MyObject)()
    
    ' now access the properties containing the dependencies
    Dim depObjA As IMyInterface = myInstance.InterfaceObject
    Dim depObjB As MyBaseClass = myInstance.BaseObject
    
  5. You can register multiple named mappings with the container for each dependency type, if required, and then use a parameter of the Dependency attribute to specify the mapping you want to use to resolve the dependent object type. For example, the following code specifies the mapping names for the Key property of the Dependency attribute for two properties of the same type (in this case, an interface) in the class MyObject.

    public class MyObject
    {
    
      private IMyInterface _objA, _objB;
    
      [Dependency("MapTypeA")]
      public IMyInterface ObjectA
      {
        get { return _objA; }
        set { _objA = value; }
      }
    
      [Dependency("MapTypeB")]
      public IMyInterface ObjectB
      {
        get { return _objB; }
        set { _objB = value; }
      }
    
    } 
    
    'Usage
    Public Class MyObject
    
      Private _objA, _objB As IMyInterface
    
      <Dependency("MapTypeA")> _
      Public Property ObjectA() As IMyInterface
        Get
          Return _objA
        End Get
        Set(ByVal value As IMyInterface)
          _objA = value
        End Set
      End Property
    
      <Dependency("MapTypeB")> _
      Public Property ObjectB() As IMyInterface
        Get
          Return _objB
        End Get
        Set(ByVal value As IMyInterface)
          _objB = value
        End Set
      End Property
    
    End Class 
    
  6. In your run-time code, register the named (non-default) mappings you require for the two concrete types that the properties will depend on, 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 two properties that have dependencies on the two classes named FirstObject and SecondObject.

    IUnityContainer uContainer = new UnityContainer()
       .RegisterType<IMyInterface, FirstObject>("MapTypeA")
       .RegisterType<IMyInterface, SecondObject>("MapTypeB");
    MyObject myInstance = uContainer.Resolve<MyObject>();
    
    // now access the properties containing the dependencies
    IMyInterface depObjA = myInstance.ObjectA;
    IMyInterface depObjB = myInstance.ObjectB;
    
    'Usage
    Dim uContainer As IUnityContainer = New UnityContainer() _
       .RegisterType(Of IMyInterface, FirstObject)("MapTypeA") _
       .RegisterType(Of IMyInterface, SecondObject)("MapTypeB")
    Dim myInstance As MyObject = uContainer.Resolve(Of MyObject)()
    
    ' now access the properties containing the dependencies
    Dim depObjA As IMyInterface = myInstance.ObjectA
    Dim depObjB As IMyInterface = myInstance.ObjectB
    

Notes on Using Property (Setter) Injection

Remember that property injection is optional and that you must apply the Dependency attribute to target class properties if you want property injection of dependent types to occur.

Using Property Injection with Constructor Parameters

You can also apply the Dependency attribute to the parameters of the constructor of a class if you only require access to these dependent objects within the constructor—though you can use code in the constructor to save the newly created dependent object instances in class-level variables if required.

This approach implements mandatory injection of dependent objects, providing that developers use the Unity container to generate the original (target) object. The dependent object instance is generated when the Unity container creates an instance of the target class using the attributed constructor.

For example, the following extracts from the StoplightPresenter class of the StopLight QuickStart sample show how the property declaration for the Schedule property defines a dependency on the StoplightSchedule class.

[Dependency]
public StoplightSchedule Schedule
{
  get { return _schedule; }
  set { _schedule = value; }
}
'Usage
<Dependency> _
Public Property Schedule() As StoplightSchedule 
  Get
    Return _schedule
  End Get
  Set(ByVal value As StoplightSchedule) 
    _schedule = value
  End Set
End Property  

The constructor of the StoplightSchedule class also contains a Dependency attribute that specifies a dependency on a concrete implementation of the IStoplightTimer class.

public StoplightSchedule([Dependency] IStoplightTimer timer)
{
  this._timer = timer;
}
'Usage
Public Sub StoplightSchedule(<Dependency()> timer As IStoplightTimer)
  Me._timer = timer
End Sub  

The following code located in the main program defines a registered mapping between the IStopLightTimer interface and the concrete implementation named RealTimeTimer.

IUnityContainer container = new UnityContainer();
container.RegisterType<IStoplightTimer, RealTimeTimer>();
'Usage
Dim container As IUnityContainer = New UnityContainer()
container.RegisterType(Of IStoplightTimer, RealTimeTimer)()

Therefore, when the main program accesses the Schedule property of the StoplightPresenter class, the Unity container will create an instance of the StoplightSchedule class. The Dependency attribute in the parameters of the StoplightSchedule class constructor will cause the Unity container to create an instance of the RealTimeTimer class.

Property Injection with Existing Objects

If you use the RegisterInstance method to register an existing object, property (setter) 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 property 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 Property (Setter) Injection

You should consider using property injection in the following situations:

  • You want to instantiate dependent objects automatically when you instantiate the parent object.
  • You want a simple approach that makes it easy to see in the code what the dependencies are for each class.
  • The parent object requires a large number of constructors that forward to each other, making debugging and maintenance difficult.
  • 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.
  • You want to make it easier for users to see what settings and objects are available, which is not possible using constructor injection.
  • 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.

Note

You can also apply property injection at run time using the configuration API of the Unity container. For more information, see Configuring Containers at Run Time.

More Information

For more information about the techniques discussed in this scenario, see the following topics: