2012-10-16 79 views
2

我有一個性能問題,因爲我使用反射和GetCustomAttributes爲我的數據訪問。性能分析器檢測到它。我有一個擴展方法是這樣的:GetCustomAttributes性能問題(表達式樹是解決方案??)

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); 
      } 
     } 
    } 
} 

我做什麼在這裏,在本質上,是映射域實體與數據庫字段,表自動數據輔助攻擊。這些實體之一的一個例子是:

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; } 

    } 

和屬性類別I:

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; 
     }    
    } 

我讀here,一個可能的改進可能是一個表達式樹,但是,第一,我是不是一個表達式tress專家,第二,我必須用.NET 3.5解決這個問題(在示例中使用.NET 4或4.5 ...)

建議?

在此先感謝。

+0

準確的瓶頸在哪裏?你在製作地圖後緩存地圖嗎?您可以將該屬性的索引映射到緩存映射類型,以便在連續調用時不必使用反射。 –

+0

盯着剖析器輸出可能是相當危險的,很容易忽略dbase查詢的成本。但是否則這是爲什麼ORM工具很受歡迎。 Linq到Sql,Entity Framework,Hibernate等等。他們只計算一個結果集到對象的映射,而不是每個數據行。 –

+0

botleneck位於GetCustomAttributes()方法中。 – Jorge

回答

4
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 
     var instance = new T(); 
     Mapper<T>.MapRow(instance, rowInstance); 
     return instance; 
    } 

    #region Nested type: Mapper 

    private static class Mapper<T> 
     where T : class 
    { 
     private static readonly ItemMapper[] __mappers; 

     static Mapper() 
     { 
      __mappers = typeof (T) 
       .GetProperties() 
       .Where(p => p.IsDefined(typeof (MappingAttribute), false)) 
       .Select(p => new 
       { 
        Property = p, 
        Attribute = p 
            .GetCustomAttributes(typeof (MappingAttribute), false) 
            .Cast<MappingAttribute>() 
            .FirstOrDefault() 
       }) 
       .Select(m => new ItemMapper(m.Property, m.Attribute)) 
       .ToArray(); 
     } 

     public static void MapRow(T instance, DataRow row) 
     { 
      foreach (var mapper in __mappers) 
      { 
       mapper.MapRow(instance, row); 
      } 
     } 

     #region Nested type: ItemMapper 

     private sealed class ItemMapper 
     { 
      private readonly MappingAttribute _attribute; 
      private readonly PropertyInfo _property; 

      public ItemMapper(PropertyInfo property, MappingAttribute attribute) 
      { 
       _property = property; 
       _attribute = attribute; 
      } 

      public void MapRow(T instance, DataRow rowInstance) 
      { 
       //TODO: Implement this with the code already provided 
      } 
     } 

     #endregion 
    } 

    #endregion 
} 

第一次擴展方法被稱爲對於給定的<T>,靜態構造將運行並緩存實例Mapper對於具有一個附加的MappingAttribute每個屬性。然後,對於之後的每個調用,它都將使用緩存的映射器來執行實際的拷貝。

您還可以製作Mapper摘要,併爲您的setValue<T>()中的每個分支使用不同的子類。這樣,大部分反射只會發生一次。

+0

謝謝詹姆斯。我重寫了你的代碼(編譯它,否則我無法測試它),並且在糾正它時有什麼錯誤,因爲性能測試比舊的MapRow更差... – Jorge

+0

我想我找到了錯誤! !正如我所想象的,我錯誤地實現了最後的MapRow(T實例,DataRow rowInstance)方法...我測試了它,並且改進得很好(時間減少了89%)謝謝! – Jorge