I have a performance problem because I use reflection and GetCustomAttributes for my data access. The performance profiler detected it. I have an extension method like this:
public static class DataRowExtensions
{
/// <summary>
/// Maps DataRow objecto to entity T depending on the defined attributes.
/// </summary>
/// <typeparam name="T">Entity to map.</typeparam>
/// <param name="rowInstance">DataRow instance.</param>
/// <returns>Instance to created entity.</returns>
public static T MapRow<T>(this DataRow rowInstance) where T : class, new()
{
//Create T item
T instance = new T();
IEnumerable<PropertyInfo> properties = typeof(T).GetProperties();
MappingAttribute map;
DataColumn column;
foreach (PropertyInfo item in properties)
{
//check if custom attribute exist in this property
object[] definedAttributes = item.GetCustomAttributes(typeof(MappingAttribute), false);
// Tiene atributos
if (definedAttributes != null && definedAttributes.Length == 1)
{
//recover first attribute
map = definedAttributes.First() as MappingAttribute;
column = rowInstance.Table.Columns.OfType<DataColumn>()
.Where(c => c.ColumnName == map.ColumnName)
.SingleOrDefault();
if (column != null)
{
object dbValue = rowInstance[column.ColumnName];
object valueToSet = null;
if (dbValue == DBNull.Value)//if value is null
valueToSet = map.DefaultValue;
else
valueToSet = dbValue;
//Set value in property
setValue<T>(instance, item, valueToSet);
}
}
}
return instance;
}
/// <summary>
/// Set "item" property.
/// </summary>
/// <typeparam name="T">Return entity type</typeparam>
/// <param name="instance">T type instance</param>
/// <param name="item">Property name to return value</param>
/// <param name="valueToSet">Value to set to the property</param>
private static void setValue<T>(T instance, PropertyInfo item, object valueToSet) where T : class, new()
{
if (valueToSet == null)
{
CultureInfo ci = CultureInfo.InvariantCulture;
if (item.PropertyType.IsSubclassOf(typeof(System.ValueType)))
{
//if is a value type and is nullable
if (item.PropertyType.FullName.Contains("System.Nullable"))
{
item.SetValue(instance, null, BindingFlags.Public, null, null, ci);
}
else
{
item.SetValue(instance, Activator.CreateInstance(item.PropertyType, null), BindingFlags.Public, null, null, ci);
}
}
else //property type is reference type
{
item.SetValue(instance, null, BindingFlags.Public, null, null, ci);
}
}
else // set not null value
{
//if is a value type and is nullable
if (item.PropertyType.FullName.Contains("System.Nullable"))
{
item.SetValue(instance, Convert.ChangeType(valueToSet, Nullable.GetUnderlyingType(item.PropertyType)), null);
}
else
{
item.SetValue(instance, Convert.ChangeType(valueToSet, item.PropertyType), null);
}
}
}
}
What I do here, in essence, is to map the domain entities with the database fields, and a data helper attacks the tables automatically. An example of one of these entities is:
public class ComboBox
{
/// <summary>
/// Represents a ComboBox item.
/// </summary>
[Mapping("CODE", DefaultValue = 0, DBType = DbParametersTypes.Varchar2, IsKey = true, IdentifierFK = "")]
public string Code { get; set; }
/// <summary>
/// Represents Text.
/// </summary>
[Mapping("DESCRIPTION", DefaultValue = "", DBType = DbParametersTypes.Varchar2, IsKey = false, IdentifierFK = "")]
public string Description { get; set; }
}
And the attribute class I use:
public sealed class MappingAttribute : Attribute
{
public string ColumnName { get; set; }
public object DefaultValue { get; set; }
public DbParametersTypes DBType { get; set; }
public bool IsKey { get; set; }
public string IdentifierFK { get; set; }
public bool IsParameter { get; set; }
public MappingAttribute(string columnName)
{
if (String.IsNullOrEmpty(columnName))
throw new ArgumentNullException("columnName");
ColumnName = columnName;
}
}
I read here that a possible improvement could be an expression tree, but, first, I’m not an expression tress expert, and second, I have to solve this with .NET 3.5…(in the sample .NET 4 or 4.5 is used…)
¿Suggestions?
Thanks in advance.
The first time the extension method is called for a given
<T>, the static constructor will run and cache an instanceMapperfor each property that has aMappingAttributeattached. Then, for every call after that, it will used the cached mappers to do the actual copy.You can also make
Mapperabstract, and use a different subclass for each branch in yoursetValue<T>(). That way most of your reflection only happens once.