Configuring Containers at Run Time

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 following sections of this topic describe the configuration of the Unity Application Block at run time in more detail:

  • Configuring Constructor, Property, and Method Injection
  • Streamlining Interception By Using PolicyDefinition API

For more information about registering objects, see Registering Existing Object Instances.

Configuring Constructor, Property, and Method Injection

You can configure the Unity container to perform injection at run time by directly creating and populating instances of the configuration classes. This provides an alternative approach to using attributes within the target classes or configuring injection requirements using the Unity configuration file.

Use the InjectionConstructor, InjectionProperty, and InjectionMethod classes in conjunction with the Configure and ConfigureInjectionFor methods to specify dependency injection parameters for the container.

Some RegisterType overloads allow for configuring injection by accepting InjectionMembers, thus enabling you to avoid having to make a seperate call to ConfigureInjectionFor on the InjectedMembers extension.

            IUnityContainer container = new UnityContainer()
            .RegisterType<AType>(new InjectionConstructor());

            AType aType = container.Resolve<AType>();
            Assert.IsTrue(aType.DefaultConstructorCalled);

The following example shows how you can configure constructor injection, property injection, and method injection using the configuration classes through the fluent interface of the container.

IUnityContainer myContainer = new UnityContainer();
myContainer.Configure<InjectedMembers>()
  .ConfigureInjectionFor<MyObject>(
    new InjectionConstructor(12, "Hello Unity!"),
    new InjectionProperty("MyProperty"),
    new InjectionProperty("MyStringProperty", "SomeText"),
    new InjectionMethod("InitializeMe", 42.0, 
            new ResolvedParameter(typeof(ILogger), "SpecialLogger"))
  );
'Usage
Dim myContainer As IUnityContainer = New UnityContainer()
myContainer.Configure(Of InjectedMembers)() _
  .ConfigureInjectionFor(Of MyObject)( _
    New InjectionConstructor(12, "Hello Unity!"), _
    New InjectionProperty("MyProperty"), _
    New InjectionProperty("MyStringProperty", "SomeText"), _
    New InjectionMethod("InitializeMe", 42.0, _
    New ResolvedParameter(GetType(ILogger), "SpecialLogger")) _
  )

The preceding code creates a container and then performs the following configuration for injection:

  • It configures constructor injection for the constructor that accepts as parameters an Integer value and a String value, and it passes the values 12 and "Hello Unity!" to these parameters.
  • It configures property injection for the property named MyProperty to use the default configuration of the container. The container resolves the value for this property using registrations and mappings within the container.
  • It configures property injection for the property named MyStringProperty to use the specified value. The container sets the property to the value "SomeText".
  • It configures method injection for the method named InitializeMe that accepts as parameters a Double value and an instance of a class that implements the ILogger interface. It passes to the first parameter the value 42.0. It also resolves the ILogger type through the container by looking for a mapping for that type with the name SpecialLogger and passes the result to the second parameter of the method.

The style of the API for dynamic injection configuration is intended to provide sufficient flexibility and still remain easy and intuitive to use.

The injection configuration APIs are based on the subclasses of InjectionParameterValue. You can also provide other types of objects when setting up injection. The objects are translated to a InjectionParameterValue according to the following rules:

  1. If the object is an instance of a subclass of the InjectionParameterValue class, the injection system uses the object.
  2. If the object is an instance of the Type class, the injection system creates a ResolvedParameter that describes how the container should perform injection to resolve an instance of that type.
  3. In all other cases, the configuration API creates an InjectionParameter instance the container uses to get the value to be injected into a property.

Note

The injection rules are located in the ToParameter method of the InjectionParameterValue class. You can examine this class to see the implementation. The InjectionConstructor, InjectionProperty, and InjectionMethod subclasses of the InjectionMember base class are used to describe the injection at the time the BuildPlan for a key is created. The BuildPlan is created the first time a key is resolved. After that, injection does not use theses three subclasses. You can create your own subclasses based on this class if you want to extend or change the behavior to suit your own specific requirements. You can also create your own subclasses based on the InjectionParameterValue class if you want to customize the handling of individual parameters.

Streamlining Interception by Using PolicyDefinition API

The streamlined InterceptionExtension.PolicyDefinition APIs provide a simplified way to wire up RuleDriven policies and their IMatchingRules and ICallHandlers. The general purpose APIs require repeated calls to the InjectedMembers.ConfigureInjectionFor and RegisterType methods. The streamlined extension APIs reduce the overhead required to manage the various strings, cross links, thus making the process more obvious and convenient.

Everything you can do with the InterceptionExtension.PolicyDefinition API can be done with the general-purpose APIs.

For more information about interception, see Using Interception with Unity.

For more information about selecting the objects and their members to add a handler pipeline, see Configuring and Using Matching Rules.

The following are streamlined configuration InterceptionExtension.PolicyDefinition methods:

  • AddPolicy. These methods are a set of methods on the interception type.
  • AddMatchingRule. These methods are on the PolicyDefinition class you get when you call AddPolicy.
  • AddCallHandler. These methods are on the PolicyDefinition class you get when you call AddPolicy.

These methods are only used for configuring rule driven policies, which are also the only policies configurable with the standard installation and setup. For user-defined policies, you must use the general purpose APIs.

The streamlined InterceptionExtension.PolicyDefinition API is similar to the expanded RegisterType API in that you can provide a lifetime manager (just like RegisterType), the mapping, and the injection configuration. It also differs in the following ways:

  • The entry point for the API is the AddPolicy method in the Interception extension.

  • The result of this method is a transient PolicyDefinition object, which can be used to AddMatchingRules and AddCallHandlers. These methods add rules and handlers to the policy, but they also configure the container as necessary.

  • The signatures for these methods are similar to those of RegisterType, but the names imply the interface being registered instead of relying on generic type parameters, as with the general-purpose RegisterTypeMethods. The following is an example.

    Instead of
    RegisterType<ICallHandler, MyCallHandler>(...) 
    you use
    AddCallHandler<MyCallHandler>(...).
    

There are three approaches for using the streamlined interception methods:

  • Supply a string parameter. Use a string parameter to indicate that you want to use an object that was configured elsewhere. If you used the general purpose API to configure a handler in the container, you would just "link" to it with this approach.

    Note

    You can supply a string and configure the corresponding rule or handler at a later time.

            public void APolicyExternallyConfiguredRulesAndHandlers()
            {
                IUnityContainer container = new UnityContainer();
                container.AddNewExtension<Interception>();
                container
                    .Configure<Interception>()
                        .AddPolicy("Foot")
                            .AddMatchingRule("rule1")
                            .AddCallHandler("handler1")
                            .AddCallHandler("handler2").Interception.Container
                    .RegisterType<IMatchingRule, AlwaysMatchingRule>("rule1")
                    .RegisterType<ICallHandler,LogCallHandler>(
                        "handler1",
                        new InjectionConstructor("handler1"))
                    .RegisterType<ICallHandler,LogCallHandler>(
                        "handler2",
                        new InjectionConstructor("handler2"),
                        new InjectionProperty("Order", 10));
                LogCallHandler.Calls.Clear();
                container
                    .Configure<Interception>()
                        .SetInjectorFor<TypeToIntercept>("wrappable", new TransparentProxyPolicyInjector());
                TypeToIntercept wrappable1 = container.Resolve<TypeToIntercept>("wrappable");
                wrappable1.Method2();
            }
    
  • Supply an instance. Use this case when you already have the object and want only the new policy to use it.

            public void APolicyGivenRulesAndHandlers()
            {
                IUnityContainer container = new UnityContainer();
                container.AddNewExtension<Interception>();
                IMatchingRule rule1 = new AlwaysMatchingRule();
                ICallHandler handler1 = new CallCountHandler();
                container
                    .Configure<Interception>()
                        .AddPolicy("Foot")
                            .AddMatchingRule(rule1)
                            .AddCallHandler(handler1);
                container
                    .Configure<Interception>()
                        .SetInjectorFor<TypeToIntercept>("wrappable", new TransparentProxyPolicyInjector());
                TypeToIntercept wrappable1 = container.Resolve<TypeToIntercept>("wrappable");
                wrappable1.Method2();
            }
    
  • Supply a type. Supply a type either as a generic type parameter or a normal parameter, and, optionally, a name, a lifetime container, and injection configuration in any combination. Use this case when you need to configure the container for the creation of a new object. If you do not specifiy a name, one is created. If a policy with the specified name exists, it will be overwritten. In this approach, you describe how to resolve for injection.

            public void APolicyGivenRulesAndHandlersTypes()
            {
                IUnityContainer container = new UnityContainer();
                container.AddNewExtension<Interception>();
                container
                    .Configure<Interception>()
                        .AddPolicy("Foot")
                            .AddMatchingRule(typeof(AlwaysMatchingRule))
                            .AddCallHandler(typeof(LogCallHandler));
                LogCallHandler.Calls.Clear();
                container
                    .Configure<Interception>()
                        .SetInjectorFor<TypeToIntercept>("wrappable", new TransparentProxyPolicyInjector());
                TypeToIntercept wrappable1 = container.Resolve<TypeToIntercept>("wrappable");
                wrappable1.Method2();
            }
    

One significant difference between the RegisterType methods and this streamlined API is that when you do not specify a name, a name is generated for you, so all "anonymous" definitions without names will not be overwritten by other definitions without names. If you do specify a name, it is used and may override previous existing policies with the same name.

You can use generic parameter support when you configure for injection, just like you do with RegisterType or ConfigureInjection.

The generic versions of the AddMatchingRule and AddCallHandler methods contain the types you can supply, unlike RegisterType. RegisterType is a general purpose method and has no constraints on the types you can supply. For AddCallHandler<TCallHandler>(), you can only provide a type value for TCallHandler that implements ICallHandler. The non-generic version of the method is limited to performing run-time checks.