2009-07-02 8 views
3

verwendet Von diesem Code kann ich wie erwartet bmwCars.CopyToDataTable() aufrufen.Ausnahme, die CopyToDataTable mit "neuer {..}" LINQ-Abfrage

var bmwCars = from car in dataTable.AsEnumerable() 
          where car.Field<string>("Make").ToLower().Equals("bmw") 
          select car; 

Aber wenn ich einige Aussage von Code ändern müssen unten, kann ich nicht nennen CopyToDataTable(), warum?

var bmwCars = from car in dataTable.AsEnumerable() 
          where car.Field<string>("Make").ToLower().Equals("bmw") 
          select new 
          { 
           Make = car.Field<string>("Make"), 
           Color = car.Field<string>("Color"), 
           PetName = car.Field<string>("PetName") 
          }; 

Antwort

5

Basierend auf Ihrer Verwendung von Field<T> erben die Objekte in dataTable (von denen ich annehme, sind vom Typ Car) erben DataRow. Dies ist erforderlich, um die Erweiterungsmethode CopyToDataTable aufzurufen. Wie Sie jedoch schreiben, geben Sie eine Enumeration eines anonymen Typs zurück, der DataRow nicht erben kann.

Also, wahrscheinlich Ihre

select new 

sollte

select new Car 

so sein, dass Sie einen IEnumerable<Car> anstelle eines IEnumerable<> von anonymen Typs sind zurück.

Abhängig von der genauen Struktur Ihrer Car Klasse, kann es notwendig sein, geringfügige syntaktische Änderungen vorzunehmen. Wenn Car öffentliche Eigenschaften hat, Make, Color und PetName dann funktioniert es wie ich vorgeschlagen habe. Wenn stattdessen Car einen Konstruktor mit Methodensignatur hat etwa gleich

public Car(string make, string color, string petName) 

dann werden Sie die LINQ-Anweisung ändern müssen sein

var bmwCars = from car in dataTable.AsEnumerable() 
       where car.Field<string>("Make").ToLower().Equals.("bmw") 
       select new Car(
        car.Field<string>("Make"), 
        car.Field<string>("Color"), 
        car.Field<string>("PetName")       
      ); 
3

Weil Sie einen neuen anonymen Typ zurückgeben, nicht das Autoobjekt selbst.

7

Sie könnten Ihre eigene CopyToDataTable zu bauen, die jede Art Takes IEnumerable (nicht nur DataRow) und gibt ein neues DataTable:

// following would not compile by default 
// because input is not an IEnumerable<DataRow> but an anonymous type 
var tblResult = bmwCars.CopyToDataTable(); 

Hier ist die Umsetzung (mit Hilfe von MSDN):

public class ObjectShredder<T> { 
    private System.Reflection.FieldInfo[] _fi; 
    private System.Reflection.PropertyInfo[] _pi; 
    private System.Collections.Generic.Dictionary<string, int> _ordinalMap; 
    private System.Type _type; 

    // ObjectShredder constructor. 
    public ObjectShredder() { 
     _type = typeof(T); 
     _fi = _type.GetFields(); 
     _pi = _type.GetProperties(); 
     _ordinalMap = new Dictionary<string, int>(); 
    } 

    /// <summary> 
    /// Loads a DataTable from a sequence of objects. 
    /// </summary> 
    /// <param name="source">The sequence of objects to load into the DataTable.</param> 
    /// <param name="table">The input table. The schema of the table must match that 
    /// the type T. If the table is null, a new table is created with a schema 
    /// created from the public properties and fields of the type T.</param> 
    /// <param name="options">Specifies how values from the source sequence will be applied to 
    /// existing rows in the table.</param> 
    /// <returns>A DataTable created from the source sequence.</returns> 
    public DataTable Shred(IEnumerable<T> source, DataTable table, LoadOption? options) { 
     // Load the table from the scalar sequence if T is a primitive type. 
     if (typeof(T).IsPrimitive) { 
      return ShredPrimitive(source, table, options); 
     } 

     // Create a new table if the input table is null. 
     if (table == null) { 
      table = new DataTable(typeof(T).Name); 
     } 

     // Initialize the ordinal map and extend the table schema based on type T. 
     table = ExtendTable(table, typeof(T)); 

     // Enumerate the source sequence and load the object values into rows. 
     table.BeginLoadData(); 
     using (IEnumerator<T> e = source.GetEnumerator()) { 
      while (e.MoveNext()) { 
       if (options != null) { 
        table.LoadDataRow(ShredObject(table, e.Current), (LoadOption)options); 
       } else { 
        table.LoadDataRow(ShredObject(table, e.Current), true); 
       } 
      } 
     } 
     table.EndLoadData(); 

     // Return the table. 
     return table; 
    } 

    public DataTable ShredPrimitive(IEnumerable<T> source, DataTable table, LoadOption? options) { 
     // Create a new table if the input table is null. 
     if (table == null) { 
      table = new DataTable(typeof(T).Name); 
     } 

     if (!table.Columns.Contains("Value")) { 
      table.Columns.Add("Value", typeof(T)); 
     } 

     // Enumerate the source sequence and load the scalar values into rows. 
     table.BeginLoadData(); 
     using (IEnumerator<T> e = source.GetEnumerator()) { 
      Object[] values = new object[table.Columns.Count]; 
      while (e.MoveNext()) { 
       values[table.Columns["Value"].Ordinal] = e.Current; 

       if (options != null) { 
        table.LoadDataRow(values, (LoadOption)options); 
       } else { 
        table.LoadDataRow(values, true); 
       } 
      } 
     } 
     table.EndLoadData(); 

     // Return the table. 
     return table; 
    } 

    public object[] ShredObject(DataTable table, T instance) { 

     FieldInfo[] fi = _fi; 
     PropertyInfo[] pi = _pi; 

     if (instance.GetType() != typeof(T)) { 
      // If the instance is derived from T, extend the table schema 
      // and get the properties and fields. 
      ExtendTable(table, instance.GetType()); 
      fi = instance.GetType().GetFields(); 
      pi = instance.GetType().GetProperties(); 
     } 

     // Add the property and field values of the instance to an array. 
     Object[] values = new object[table.Columns.Count]; 
     foreach (FieldInfo f in fi) { 
      values[_ordinalMap[f.Name]] = f.GetValue(instance); 
     } 

     foreach (PropertyInfo p in pi) { 
      values[_ordinalMap[p.Name]] = p.GetValue(instance, null); 
     } 

     // Return the property and field values of the instance. 
     return values; 
    } 

    public DataTable ExtendTable(DataTable table, Type type) { 
     // Extend the table schema if the input table was null or if the value 
     // in the sequence is derived from type T.    
     foreach (FieldInfo f in type.GetFields()) { 
      if (!_ordinalMap.ContainsKey(f.Name)) { 
       // Add the field as a column in the table if it doesn't exist 
       // already. 
       DataColumn dc = table.Columns.Contains(f.Name) ? table.Columns[f.Name] 
        : table.Columns.Add(f.Name, f.FieldType); 

       // Add the field to the ordinal map. 
       _ordinalMap.Add(f.Name, dc.Ordinal); 
      } 
     } 
     foreach (PropertyInfo p in type.GetProperties()) { 
      if (!_ordinalMap.ContainsKey(p.Name)) { 
       // Add the property as a column in the table if it doesn't exist 
       // already. 
       DataColumn dc = table.Columns.Contains(p.Name) ? table.Columns[p.Name] 
        : table.Columns.Add(p.Name, p.PropertyType); 

       // Add the property to the ordinal map. 
       _ordinalMap.Add(p.Name, dc.Ordinal); 
      } 
     } 

     // Return the table. 
     return table; 
    } 
} 

Jetzt können Sie diese Erweiterungen hinzufügen:

public static class CustomLINQtoDataSetMethods { 
    public static DataTable CopyToDataTable<T>(this IEnumerable<T> source) { 
     return new ObjectShredder<T>().Shred(source, null, null); 
    } 

    public static DataTable CopyToDataTable<T>(this IEnumerable<T> source, 
               DataTable table, LoadOption? options) { 
     return new ObjectShredder<T>().Shred(source, table, options); 
    } 
} 

Voilà! Jetzt CopyToDataTable funktioniert mit jeder Art von IEnumerable<T> :)

Verwandte Themen