Extending Unity Container to Support DefaultValue Attribute






4.84/5 (11 votes)
Extending Microsoft Enterprise Library Unity block to support custom attributes and DefaultValueAttribute in particular.
Introduction
DefaultValueAttribute
is an attribute which allows decorating of classes’ properties with default values. It specifies what initial constant values should be used for initialization of these properties when the Type is instantiated. Unfortunately, neither C# nor .NET provides any support for this attribute. In my previous article, I've examined several implementations of initialization code to address this issue. All of these methods require a call from within class constructor to initialize its members. This article demonstrates how to extend Unity framework to enable support for this attribute and eliminate requirement to call initialization method from within the constructor.
Background
System.ComponentModel
.
DefaultValueAttribute
is a class derived from the Attribute
class and can be used to decorate properties of class types with constant default values. More information about this class can be found at MSDN
library. A typical example of this attribute usage would be like this:
public class TestClass
{
...
[DefaultValue(1)]
public int IntProperty { get; set; }
[DefaultValue(1)]
public long LongProperty { get; set; }
[DefaultValue(true)]
public bool BoolProptrty { get; set; }
}
A Unity container was designed with separation of concerns paradigm in mind and as a result, it provides a way to move certain aspects to outside of classes. This allows eliminating a call to initialization procedure from within the class constructors and implementing initialization using AOP style.
Extension Methods Implementation
The easiest and most straightforward way to implement this functionality would be to use Unity’s public
API and Extension Methods.
Unity container provides a mechanism to specify name of the class’ property and initial value. This property should be initialized with when the class is resolved. This functionality is implemented with the help of InjectionProperty
classes.
In order to support DefaultValueAttribute
, all that needs to be done is to create instance of InjectionProperty
class for each of the specified DefaultValue
attributes and pass array of these InjectionProperty
objects to RegisterType
method:
ArrayList defValDepends = new ArrayList();
foreach (PropertyInfo prop in _this.GetProperties(BindingFlags.Instance |
BindingFlags.Public))
{
// Get default attribute and register dependency
DefaultValueAttribute[] defValue =
prop.GetCustomAttributes(typeof(DefaultValueAttribute), false)
as DefaultValueAttribute[];
defValDepends.Add(new InjectionProperty(prop.Name, defValue[0].Value)));
}
// This if Unity 2.0 syntax
RegisterType<TFrom, TTo>
((InjectionMember[])defValDepends.ToArray(typeof(InjectionMember));
*This code is simplified for clarity purposes.
The benefit of this implementation is simplicity and use of public
API. Unfortunately, it requires explicit call to Extension method for every class whose properties need to be initialized:
IUnityContainer container = new UnityContainer()
.RegisterTypeWithDefaultValues<IMockClassInterface, MockClass>();
Where RegisterTypeWithDefaultValues
is an Extension Method.
Writing a Unity Container Extension
Calling support for DefaultValueAttribute
through wrapper method is an acceptable but inconvenient way. It introduces dependency to concrete implementation and hard-codes this dependency into the source. In order to eliminate this dependency and provide DefaultValue
support for all types registered with container, this functionality should be implemented as an extension to the Unity container. For more information about extending Unity block, please visit this site.
Requirements
In order to create an extension for Unity container, two Types have to be created:
- A unique interface derived from
IUnityContainerExtensionConfigurator
- A class derived from
UnityContainerExtension
and implementing configuration interface.
public class UnityDefaultValueExtender :
UnityContainerExtension, IUnityDefaultValueExtender
{
…
}
public interface IUnityDefaultValueExtender : IUnityContainerExtensionConfigurator { }
Architecture
Unity containers allow expanding functionality by adding extensions with a call to AddExtension
or AddNewExtension
methods. Extensions could also be configured in configuration files but coverage of this topic is outside of the scope of this article. For more information, please see MSDN documentation.
During initialization of the extension container calls Initialize()
method of the extension class and allows it to initialize and configure itself. Inside Initialize()
method, extension class retrieves reference to a container and attaches event handler to Registering
event exposed by IUnityContainer
interface.
protected override void Initialize()
{
base.Context.Registering += new EventHandler<RegisterEventArgs>(this.OnRegister);
}
Operation
When a type is being registered with Unity container, among other things, it iterates through each property of that class and creates policies which control how these properties are resolved and initialized. InjectionProperty
objects passed to RegisterType
method add or override default policies and provide value information for property initialization.
In order to provide support for DefaultValueAttribute
, all that needs to be done is to create a set of InjectionProperty
objects. Initialize them with property name and value information from DefaultValue
attributes, and add policies related to these InjectionProperty
objects to policy collection.
private void OnRegister(object sender, RegisterEventArgs e)
{
ArrayList defValDepends = new ArrayList();
IUnityContainer container = sender as IUnityContainer;
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(e.TypeTo))
{
// Get default attribute and register dependency
DefaultValueAttribute defValue = prop.Attributes[typeof(DefaultValueAttribute)]
as DefaultValueAttribute;
// Create injection property info and init it from DefaultValueAttribute
InjectionProperty iProp =
new InjectionProperty(prop.DisplayName, defValue.Value);
// Add related policies
iProp.AddPolicies(e.TypeTo, e.Name, base.Context.Policies);
}
}
*This code is simplified for clarity purposes.
Next time a new object of this type is resolved, the container will initialize properties decorated with DefaultValue
attributes with all the correct values.
Using the Code
This article contains two downloads:
- A Visual Studio test project demonstrating operation of the extension
- UnityDefaultValueExt.cs source file containing implementation of the extension
In order to use the extension, please follow these steps:
- Download UnityDefaultValueExt.zip, unzip and add file UnityDefaultValueExt.cs to your project.
- Bring namespace
UnityDefaultValueExtension
into scope by adding line:using UnityDefaultValueExtension;
to your class’ source file.
- Add statement
your_container.AddNewExtension<UnityDefaultValueExtender>();
or apply appropriate configuration settings to your config file.
IUnityContainer container = new UnityContainer() .AddNewExtension<UnityDefaultValueExtender>()
- This implementation defaults to Unity 2.x and Enterprise Library 5.xx. In order to use this code with earlier versions of Unity block (Enterprise Library 4.1 or earlier) symbol
LEGACY_CODE
must be defined in Project->Properties->Conditional Computational Symbols.
History
- March 28, 2010 - Article published