Dynamic, Object to Object Converter






2.29/5 (3 votes)
How to use attributes to create a dynamic converter so that you do not need to create a new converter for every conversion.
Introduction
In this article I hope to show you how to use attributes to create a dynamic converter so that you do not need to create a new converter for every conversion. I developed this to facilitate me in converting a DTO object to a Domain (Entity) object.
What this article assumes you know or have:
- You can read C# code.
- You have some version of .NET Framework installed, my program demo uses Framework 2.0.
- You have some idea of attributes and their use.
What a Converter is and Where it is Useful
A converter in general terms is a class used to facilitate he conversion of one object to another. When using a Test Driven Model View Presenter Pattern I needed to convert DTO objects to Domain (Entity) objects and vice versa. Before I had to create a converter for every conversion, suppose there were 500 objects to be converted, I would have had to create 500 converters that would be messy and would mean lot of maintenance in the future.
Project Guidelines
The project we will create shall include defining custom Attributes, Converter class, and a Test class.
If you are using VS.NET create a new project called ‘Converter’ and add a folder ‘Attributes’ to it. Within the folder create a class ValueFromPropertyAttribute.cs
. Copy the code from below.
ValueFromPropertyAttribute.cs
using System;
namespace Amyn.Converter.Attributes
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple=false)]
public class ValueFromPropertyAttribute : Attribute
{
private string _propertyName = string.Empty;
private System.Type _fromType = null;
private System.Type _toType = null;
public ValueFromPropertyAttribute(string propertyName)
{
this._propertyName = propertyName;
}
public ValueFromPropertyAttribute(string propertyName, System.Type
fromType)
{
this._propertyName = propertyName;
this._fromType = fromType;
}
public ValueFromPropertyAttribute(string propertyName, System.Type fromType,
System.Type toType)
{
this._propertyName = propertyName;
this._fromType = fromType;
this._toType = toType;
}
public string ValueFromProperty { get { return this._propertyName; } }
public System.Type FromType { get { return this._fromType; } }
public System.Type ToType { get { return this._toType; } }
} }
That’s all there in the creation of the Attribute
class. As you can see it's just a class that inherits from System.Attribute
, and has three properties.
Now let’s create the Converter
class.
Converter.cs
using System;
using System.Collections;
using System.Reflection;
using Amyn.Converter.Attributes;
namespace Amyn.Converter
{
public static class Converter
{
public static object ConvertAll(object[] fromObjects, System.Type
toType)
{
if (fromObjects != null)
{
ArrayList list = new ArrayList(fromObjects.Length);
foreach (object obj in fromObjects)
{
list.Add(Convert(obj, toType));
}
return list.ToArray(toType);
}
return null;
}
public static object Convert(object fromObject, System.Type toType)
{
if (fromObject == null) return null;
object returnObject = Activator.CreateInstance(toType);
PropertyInfo [] infos = returnObject.GetType().GetProperties();
foreach (PropertyInfo property in infos)
{
ValueFromPropertyAttribute [] attributes =
(ValueFromPropertyAttribute
[])property.GetCustomAttributes(typeof
(ValueFromPropertyAttribute), false); false);
if (attributes.Length > 0)
{
PropertyInfo fromProperty =
fromObject.GetType().
GetProperty(attributes[0].ValueFromProperty);
if (attributes[0].FromType == null)
{
property.SetValue(returnObject,
fromProperty.GetValue
(fromObject, null), null);
}
else
{
if (fromProperty.PropertyType.IsArray)
{
property.SetValue(returnObject,
ConvertAll((object [])
fromProperty.GetValue(fromObject, null),
attributes[0].ToType), null);
}
else
{
property.SetValue(returnObject,
Convert(fromProperty.GetValue(fromObject,
null), property.PropertyType), null);
}
}
}
}
return returnObject;
}
}
}
The Converter
class has two public methods:
ConvertAll
which takes two parameters, an array of objects to convert from ‘fromObjects
’, and Type of the object to convert to ‘toType
’. It returns an ArrayList
of objects of type toType
after conversion. Inside the body it checks for null of fromObjects
. If not null it iterates through the fromObjects
array and calls the Convert
method on each iteration.
The Convert
method does the bulk of the job. It takes an object to convert from ‘fromObject
’ and Type of object to convert to ‘toType
’. It returns an object of Type toType
after conversion. The System.Activator.CreateInstance
method creates an instance of the specified Type (toType
) using that Type’s default constructor. System.Reflection.PropertyInfo
is used to discover the attributes of the property and provide access to its metadata. All the work is done by the framework with the call to GetCustomAttributes
on the Type object. GetCustomAttributes
takes two parameters, the first is a Type object for the attribute we wish to get, the second is a Boolean telling the framework whether it should look at the types that this class derives from. GetCustomAttributes
returns an object array containing each of the attributes found that match the Type passed in. In this case we request all attributes of a specific type, so we can safely cast that to an array of our attribute ‘ValueFromPropertyAttribute
’. Once it has got an array of ‘ValueFromPropertyAttribute
’ it determines how many are in the array, if there are none in the array we know that the class does not have the ‘ValueFromPropertyAttribute
’ applied to it, else we take the first one in the array, using PropertyInfo.SetValue
and PropertyInfo.GetValue
we set the properties of the return object.
Let's now create a class to test our Dynamic Custom Converter. Create a new project in the solution and call it TestHarness
.
TestHarness
using System;
using Amyn.Converter;
using Amyn.Converter.Attributes;
namespace TestHarness
{
class Program
{
static void Main(string[] args)
{
FromTestClass[] testArray = new FromTestClass[2]
{
new FromTestClass()
{
FromPropertyBool=true,
FromPropertyString="TestString 1",
FromPropertyClass=new FromTestClass[1]
{
new FromTestClass(){ FromPropertyBool=false,
FromPropertyString="String inner",
FromPropertyClass=null }
}
},
new FromTestClass()
{
FromPropertyBool=true,
FromPropertyString="Test String 2" ,
FromPropertyClass=new FromTestClass[2]
{
new FromTestClass()
{
FromPropertyBool=false,
FromPropertyString="String inner",
FromPropertyClass=new FromTestClass[1]
{
new FromTestClass(){
FromPropertyBool=false,
FromPropertyString="String inner
inner", FromPropertyClass=null }
}
},
new FromTestClass(){ FromPropertyBool=false,
FromPropertyString="String inner",
FromPropertyClass=null }
}
}
};
ToTestClass [] returnAll = (ToTestClass [])Converter.ConvertAll(testArray,
typeof(ToTestClass));
foreach (ToTestClass returnConverted in returnAll)
{
Console.WriteLine(returnConverted.ToPropertyString); Console.WriteLine(
returnConverted.ToPropertyBool.ToString());
}
Console.ReadLine();
}
}
class FromTestClass
{
private string fromPropertyString;
private bool fromPropertyBool;
private FromTestClass [] _fromClass;
public FromTestClass() { }
public string FromPropertyString { get { return this.fromPropertyString; } set {
fromPropertyString = value; } }
public bool FromPropertyBool { get { return this.fromPropertyBool; } set {
fromPropertyBool = value; } }
public FromTestClass [] FromPropertyClass { get { return this._fromClass; } set {
this._fromClass = value; } }
}
class ToTestClass
{
private string toPropertyString;
private bool toPropertyBool;
private ToTestClass [] _testClass;
public ToTestClass() { }
[ValueFromProperty("FromPropertyString")]
public string ToPropertyString { get { return this.toPropertyString; } set {
toPropertyString = value; } }
[ValueFromProperty("FromPropertyBool")]
public bool ToPropertyBool { get { return this.toPropertyBool; } set {
toPropertyBool = value; } }
[ValueFromProperty("FromPropertyClass", typeof(FromTestClass),
typeof(ToTestClass))]
public ToTestClass [] ToPropertyClass{ get { return this._testClass; } set {
this._testClass = value; } }
}
}
Summary
In the present development scenario, Pattern designing is an essential way of developing projects. In Model View Pattern conversion of objects from one type to another is a day to day need. Decorate the properties of the class to convert to with the custom attribute and the conversion is done.