Configuration Files for Interception
Unity 3 treats interception like any extension you add to Unity. As with any extension in Unity 3, the Unity interception mechanism can be configured through either the API or through a Unity configuration section.
Note
Unity provides partial backward compatibility for implementing interception through a container. Earlier versions used a container extension named InterceptionExtension, which resides in the assembly named Microsoft.Practices.Unity.Interception.dll. To configure interception, you specify this extension in the <extensions> element of your application configuration, and then define the behavior of interception in the <extensionConfig> section.
Using the extension and register elements in Unity 3 is comparable to Interceptor element use in the extensionConfig section in earlier versions.
For more information on backward compatibility, see Reusing Configuration Files Based on a Previous Schema.
For more information about Unity 1.2 interception, see Using Interception with Unity on MSDN.
This topic contains the following sections to describe the interception configuration file:
- Using the Configuration File to Enable Interception
- Standard Interception Aliases
- Enabling Interception of a Type
- Configuring Policy Injection Policies
- Legacy Interception Configuration
- Interception Configuration Schema Elements
- Registering Interception at Run Time
Using the Configuration File to Enable Interception
Interception is not part of the default Unity configuration schema. Before you can configure interception you must add the correct <sectionExtension> element to your configuration section in the configuration file. The interception configuration extension is the type Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, in the Microsoft.Practices.Unity.Interception.Configuration assembly.
The following extract from a configuration file adds the interception extension, InterceptionConfigurationExtension, by using the <sectionExtension> element.
<unity xmlns="https://schemas.microsoft.com/practices/2010/unity">
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
</unity>
Loading the interception section extension supplies a set of Standard Interception Aliases and additional configuration elements.
Loading the section extension only enables the interception configuration to be given in the configuration file. Interception itself will not work unless you also load the interception container extension in your Unity container instance. This can also be done in the configuration file, as shown in the following example.
<unity xmlns="https://schemas.microsoft.com/practices/2010/unity">
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
<container>
<extension type="Interception" />
</container>
</unity>
You do not need to explicitly alias the interception container extension’s type or add a <namespace> or <assembly> element. When you add the InterceptionConfigurationExtension to the <sectionExtension> element, the alias "Interception" is automatically added, along with the other default aliases for common types used in interception configuration.
Standard Interception Aliases
The following table contains the list of predefined type aliases provided by the Unity interception extension. All of these types are in the Microsoft.Practices.Unity.Interception.dll assembly and the Microsoft.Practices.Unity.InterceptionExtension namespace.
Alias |
Description |
---|---|
Interception |
The interception container extension. |
IInterceptionBehavior |
Interface for interception behaviors. |
PolicyInjectionBehavior |
Behavior implementing policy injection. |
Policy Injection Types |
Policy injection types. |
ImatchingRule |
The matching rule interface. |
IcallHandler |
The policy injection call handler interface. |
Policy Injection Matching Rules |
Policy injection matching rules. |
AssemblyMatchingRule |
Match based on being in a particular assembly. |
CustomAttributeMatchingRule |
Match based on having a given attribute. |
MemberNameMatchingRule |
Match based on member name. |
ParameterTypeMatchingRule |
Match based on parameter types. |
PropertyMatchingRule |
Match based on a property name. |
TagAttributeMatchingRule |
Match based on having the Tag attribute. |
TypeMatchingRule |
Match based on type. |
Interceptor Types |
Interceptor type. |
VirtualMethodInterceptor |
Virtual method type interceptor. |
InterfaceInterceptor |
Interface interceptor. |
TransparentProxyInterceptor |
Transparent proxy interceptor. |
Enabling Interception on a Type
To turn on interception for a type in the container, you specify the interceptor and behaviors using the following child elements of the <register> element.
- <interceptor>
- <interceptionBehavior>
- <policyInjection>
- <addInterface>
The following example turns on interception using the transparent proxy interceptor and performs policy injection.
<unity xmlns="https://schemas.microsoft.com/practices/2010/unity">
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
<container>
<extension type="Interception" />
<register type="MyType">
<!-- Other children, like constructor or property -->
<interceptor type="TransparentProxyInterceptor" />
<policyInjection />
</register>
</container>
</unity>
After loading this configuration, any objects of type MyType will be intercepted by using the transparent proxy interceptor and they will have the policy injection behavior applied to them.
Configuring Policy Injection Policies
Policy injection polices can also be configured through the Unity configuration section. This section describes how to perform the configuration along with several options you can use when defining policies.
Policies are defined on a per-container basis inside an <interception> element. The following example defines two policies, with matching rules and a call handler applied.
<unity xmlns="https://schemas.microsoft.com/practices/2010/unity">
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
<container>
<extension type="Interception" />
<interception>
<policy name="addDataAccessTypes">
<matchingRule name="DataLayerMatch" type="NamespaceMatchingRule">
<constructor>
<param name="namespaceName" value="MyApp.DataAccess" />
</constructor>
</matchingRule>
<callHandler name="LogHandler" type="LoggingCallHandler" />
<callHandler name="SecurityHandler"
type="DatabaseSecurityCheckHandler" />
</policy>
<policy name="webMethods">
<matchingRule name="MatchWebRequestMethods" />
<callHandler name="LogWebMethodHandler" type="LoggingCallHandler" />
</policy>
</interception>
<register type="IMatchingRule" name="MatchWebRequestMethods"
mapTo="MemberNameMatchingRule">
<constructor>
<param name="nameToMatch" value="Begin*Request" />
</constructor>
</register>
</container>
</unity>
This example demonstrates the two most common approaches for defining matching rules and call handlers in a configuration file. The first approach, the inline style, declares the type and injection members directly within the policy definition. This is what was used in the first matching rule declaration, as shown in the following extract from the previous example.
...
<matchingRule name="DataLayerMatch" type="NamespaceMatchingRule">
<constructor>
<param name="namespaceName" value="MyApp.DataAccess" />
</constructor>
</matchingRule>
...
This approach is designed to work much like a <register> element. The only difference is that you cannot provide a type mapping. The underlying type given to the container is always IMatchingRule. Any valid child element for the <register> element can be used with the <matchingRule> element (including <lifetime>, <constructor>, and <property> elements). In this example, the actual matching rule type is NamespaceMatchingRule, and when the matching rule object must be created, it is created by calling the constructor that takes a single string parameter named namespaceName. The value for that will be MyApp.DataAccess. The intent of this matching rule configuration is to match all methods on the types in that namespace.
The same design applies to the <callHandler> element, except that you are defining injection for a call handler (a type that implements ICallHandler) instead of a matching rule.
The second most common approach is to use a named reference, as in the following example with the MatchWebRequestMethods named reference.
...
<matchingRule name="MatchWebRequestMethods" />
...
<register type="IMatchingRule" name="MatchWebRequestMethods"
mapTo="MemberNameMatchingRule">
<constructor>
<param name="nameToMatch" value="Begin*Request" />
</constructor>
</register>
...
In this approach, a type of matching rule is not specified. Instead, a name is provided. At configuration time, the container will attempt to resolve an IMatchingRule with the name MatchWebRequestMethods that you specified. The result is that the container will look for a registration for the type ICallHandler with the name MatchWebRequestMethods. That registration is defined a few lines lower in the previous example file, which works because the order of definition in the file does not matter. The same design applies to the <callHandler> element.
The name is always required by the configuration system. However, when using an inline style registration, the name is ignored in the container.
The approach you choose to use is a matter of personal preference. The following are factors to consider when choosing an approach to use for policies, matching rules, and call handlers in your configuration file:
- If your policies are fairly small and initialization of the matching rules and call handlers is straightforward, the inline style will be the simplest approach. Everything related to your policies is then in one place.
- If you want to reuse a call handler definition or matching rule across policies, a named reference will facilitate this approach. Using a named reference lets you define the configuration for the call handler or matching rule in a single place and simply reference it from the policies. Choosing the name to describe what the matching rule or call handler’s purpose is makes it easier to track them; for example, MatchWebRequestMethods is clearly for matching methods that do Web requests.
- If you have a large number of policies or many matching rules and call handlers, using named references will facilitate your work. Using the inline style can quickly bloat your policy definitions, making it hard to tell what the policies are and which ones are grouped together.
For more information on registering interception at run time, see Registering Interception.
For more information on registering policies, matchingRules, and callHandlers at run time see Registering Policy Injection Components.
Legacy Interception Configuration
In earlier versions of Unity, enabling interception on a type was done using a separate element entirely. Unity 3 supports this approach as well, in order to allow some reuse of other configuration files. The syntax is not identical, so you will still need to copy and update your configuration file; you cannot use an older Unity configuration file directly. For new development, configuring interception through the <register> element as explained above is the recommended approach.
The <interceptors> element can appear within a <container> element, and you can then specify a series of interceptors and the types they should intercept.
<container>
<extension type="Interception" />
<interceptors>
<interceptor type="VirtualMethodInterceptor">
<default type="wrappableVirtual"/>
</interceptor>
<interceptor type="TransparentProxyInterceptor">
<key type="wrappable"/>
</interceptor>
<interceptor type="TransparentProxyInterceptor">
<key type="wrappable" name="name"/>
</interceptor>
</interceptors>
</container>
The <interceptor> element lets you specify a single interceptor type. You can use the child elements of <interceptor> to specify the type that this interceptor should be applied to. See <the <default> Element and the <key> Element for more information.
When defining interception through the <interceptors> element, policy injection is automatically enabled for the types applied, and you cannot add any additional interception behaviors.
For more information on backward compatibility, see Reusing Configuration Files Based on a Previous Schema.
For more information about Unity 1.2 interception, see Using Interception with Unity on MSDN.