Monday, 3 November 2008

Using Attributes, Reflection and GetCustomAttributes() to support field mappings between systems

My task today was to generate MS word documents using Aspose.Word and merge fields. The problem was, the names of the merge fields did not match exactly with the names of the properties of my objects - and I didn't want to map all fields in my objects to the external components in a separate method call. I approached this with a mechanism I have used several times before - using CustomAttributes and Reflection to drive the property mappings between application entities and external components such as Documents or Emails. See below for details on this. Note the use of the GetCustomAttributes() for retrieving custom attributes on your properties.

The attribute class:



/// <summary>
/// Supports the mapping between Aspose.Word Merge fields and the Fields
/// in the Data Transfer Object
/// </summary>
[AttributeUsage(AttributeTargets.All)]
public class DocumentMergeFieldMappingAttribute : System.Attribute
{
public string MergeFieldName { get; set; }

public DocumentMergeFieldMappingAttribute(string mergeFieldName)
{
MergeFieldName = mergeFieldName;
}
}


The mapping class which builds up a Dictionary (name, value pairs)


/// <summary>
/// Gets dictionary of field values for merge into documents - uses the
/// DocumentMergeFieldMappingAttribute to determine which dto properties
/// should render to the Document itself.
/// </summary>
/// <param name="inputDto"></param>
/// <returns></returns>
private IDictionary<string, IFieldValueDto> GetDtoFieldValues(object inputDto)
{
Dictionary<string, IFieldValueDto> dictionary = new Dictionary<string, IFieldValueDto>();

Type objectType = inputDto.GetType();
PropertyInfo[] properties = objectType.GetProperties();

foreach(PropertyInfo property in properties)
{
foreach (Attribute attribute in property.GetCustomAttributes(true))
{
if (attribute is DocumentMergeFieldMappingAttribute)
{
string fieldValue = property.GetValue(inputDto, null) as string ?? string.Empty;
//Set property value and the mapped document field name
dictionary.Add(((DocumentMergeFieldMappingAttribute)attribute)
.MergeFieldName, new FieldValueDto<string>(fieldValue));
}
}
}
return dictionary;
}

No comments: