Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C#

Extending Unity Container to Support DefaultValue Attribute

Rate me:
Please Sign up or sign in to vote.
4.84/5 (11 votes)
3 Apr 2012CPOL4 min read 41.2K   509   19   6
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:

C#
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:

C#
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:

C#
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:

  1. A unique interface derived from IUnityContainerExtensionConfigurator
  2. A class derived from UnityContainerExtension and implementing configuration interface.
C#
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.

C#
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.

C#
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:

  1. Download UnityDefaultValueExt.zip, unzip and add file UnityDefaultValueExt.cs to your project.
  2. Bring namespace UnityDefaultValueExtension into scope by adding line:
    C#
    using UnityDefaultValueExtension;

    to your class’ source file.

  3. Add statement
    C#
    your_container.AddNewExtension<UnityDefaultValueExtender>(); 

    or apply appropriate configuration settings to your config file.

    C#
    IUnityContainer container = new UnityContainer() 
          .AddNewExtension<UnityDefaultValueExtender>() 
  4. 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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Senior Software Engineer with over 20+ years of experience in variety of technologies, development tools and programming languages.

Microsoft Certified Specialist programming in C#, JavaScript, HTML, CSS

Comments and Discussions

 
GeneralXamlServices.Load with Unity Pin
xiety66628-May-10 3:55
xiety66628-May-10 3:55 
GeneralRe: XamlServices.Load with Unity Pin
Eugene Sadovoi28-May-10 6:00
Eugene Sadovoi28-May-10 6:00 
GeneralRe: XamlServices.Load with Unity Pin
xiety66630-May-10 20:12
xiety66630-May-10 20:12 
GeneralRe: XamlServices.Load with Unity Pin
Eugene Sadovoi7-Jun-10 10:17
Eugene Sadovoi7-Jun-10 10:17 
In the code you've provided no Unity container used to resolve and instantiate the class. For this scenario I would recomend initialization method described in this article: DefaultValue attribute based approach to property initializaton
QuestionWhat's the advantage? Pin
Klaus Luedenscheidt29-Mar-10 19:01
Klaus Luedenscheidt29-Mar-10 19:01 
AnswerRe: What's the advantage? Pin
Eugene Sadovoi29-Mar-10 20:43
Eugene Sadovoi29-Mar-10 20:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.