2016-11-14 4 views
1

Ich habe eine generische Methode geschrieben, die eine Sammlung von generischen Typen aus einer Datentabelle generiert. Ich habe nach verschiedenen Implementierungen gesucht, aber die meisten von ihnen waren sehr leistungsschwach, wenn es um viele Eigenschaften und viele Datensätze ging. Dieser läuft bisher recht gut.Ignorieren Eigenschaften mit benutzerdefinierten Attributsatz, mit Reflexion

Ich habe versucht, die Methode zu verbessern, indem ich ein benutzerdefiniertes Attribut (DataField) über den Eigenschaften hinzufüge, auf diese Weise könnte ich es einfach in die Eigenschaften aufnehmen und ich kann es mit den Spalten übergehen oder einen benutzerdefinierten Namen angeben für die Eigenschaft, die dem Spaltennamen des Datasets entspricht.

Ich schaute auf den Code und es sieht aus wie eine riesige Sauerei jetzt bin ich wirklich nicht stolz darauf und ich möchte eine schönere Umsetzung haben. Kann mir jemand ein paar Tipps geben? Würde es sehr schätzen.

Versucht, um Kommentare nicht sicher, wie viel es geholfen hat. Vielen Dank, hier ist der Code:

private static void SetItemFromRow<T>(T item, DataRow row) where T : new() 
    { 
     // Get all properties with attributes. 
     PropertyInfo[] propWithAttributes = item.GetType().GetProperties().Where(x => Attribute.IsDefined 
      (x, typeof(DataField))).ToArray(); 

     foreach (DataColumn col in row.Table.Columns) 
     { 
      // Find property that matches the column name. 
      PropertyInfo p = item.GetType().GetProperty(col.ColumnName); 
      bool ignoreProperty = false; 

      if (p != null) 
      { 
       // If no attribute exists set the property value. Break out from the loop to go to the next column (Property). 
       if (!propWithAttributes.Contains(p)) 
       { 
        if (row[col] != DBNull.Value) 
        { 
         p.SetValue(item, row[col], null); 
         continue; 
        } 
       } 

       // If the property has a custom attribute then check if its ignore property is true. If so we break out from the loop and go to the next column (Property). 
       var attrs = p.GetCustomAttributes(typeof(DataField), false).ToArray() as DataField[]; ; 

       if (attrs != null) 
        foreach (var attr in attrs) 
        { 
         if (attr.Ignore) 
          ignoreProperty = true; 
        } 

       if (ignoreProperty) continue; 
      } 

      SetPropertyWithCustomName(item, propWithAttributes, row, col);  
     } 
    } 

Jetzt haben wir alle auf das Objekt festgelegt Eigenschaften, die einen entsprechenden Spaltennamen hatte, auch alle Eigenschaften, die wir übersprungen, die wir ignorieren wollten. Der letzte Schritt besteht darin, die Eigenschaften festzulegen, für die ein DataField-Attribut mit dem Namen definiert ist.

  private static void SetPropertyWithCustomName<T>(T item, PropertyInfo[] propWithAttributes, DataRow row, DataColumn col) 
     where T : new() 
    { 

     foreach (var prop in propWithAttributes) 
     { 
      // Get the attributes for the property. 
      var attrs = prop.GetCustomAttributes(typeof(DataField), false).ToArray() as DataField[]; 
      bool match = false; 

      if (attrs != null) 
      { 
       foreach (var attr in attrs) 
       { 
        // Check if the column name matches the custom name on the property. 
        if (col.ColumnName == attr.Name) 
        { 
         var p = item.GetType().GetProperty(prop.Name); 
         if (row[col] != DBNull.Value) 
         { 
          p.SetValue(item, row[col], null); 
          match = true; 
          break; 
         } 
        } 
       } 

      if (match) break; 

      } 
     } 
    } 
+0

Definieren * nicier *? Einfacher zu lesen? Schneller? Speicher effizienter? In jedem Fall ist Code Review [dort] (http://codereview.stackexchange.com/). – Sinatr

+0

Nun besser lesbar wäre für jetzt. –

+0

Danke ich werde es mal "da" versuchen. –

Antwort

0

ist hier ein bisschen besser lesbare Version des Codes (wenn ich die Absicht richtig verstehe):

private static readonly Dictionary<Type, DataFieldProperty[]> _propsCache = new Dictionary<Type, DataFieldProperty[]>(); 
private static DataFieldProperty[] GetProperties(Type type) { 
    lock (_propsCache) { 
     if (!_propsCache.ContainsKey(type)) { 
      var result = new List<DataFieldProperty>(); 
      foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { 
       var attr = prop.GetCustomAttribute<DataField>(); 
       result.Add(new DataFieldProperty { 
        Name = attr?.Name ?? prop.Name, 
        Ignore = attr?.Ignore ?? false, 
        Property = prop 
       }); 
      } 
      _propsCache.Add(type, result.ToArray()); 
     } 
     return _propsCache[type]; 
    } 
} 

private class DataFieldProperty { 
    public string Name { get; set; } 
    public PropertyInfo Property { get; set; } 
    public bool Ignore { get; set; } 
} 

private static void SetItemFromRow<T>(T item, DataRow row) where T : new() { 
    // Get all properties with attributes. 
    var props = GetProperties(item.GetType()); 
    foreach (DataColumn col in row.Table.Columns) { 
     // Find property that matches the column name. 
     var p = props.FirstOrDefault(c => c.Name == col.ColumnName && !c.Ignore); 
     if (p != null) { 
      if (row[col] != DBNull.Value) { 
       p.Property.SetValue(item, row[col], null); 
      } 
     } 
    } 
} 

Beachten Sie, dass ich es nicht wirklich laufen (aber überprüft es kompiliert).

+0

Danke, dass du dir die Zeit genommen hast, ich werde es in einer Sekunde testen und Feedback geben. –

+0

Hallo, es ist sicherlich viel weniger Code und weniger für jeden verschachtelt. Ich habe es getestet und funktioniert perfekt, ich hatte nicht vs2015 und C# 6 also musste kleine Anpassungen tun, aber ja, es funktioniert! Vielen Dank ! –

+0

Gern geschehen. GetProperty-Aufrufe werden ebenfalls zwischengespeichert. Bei großen Sätzen sollte es daher etwas schneller sein. – Evk

Verwandte Themen