Excepción utilizando CopyToDataTable con “nueva {..}” consulta LINQ

Desde este código puedo llamar a bmwCars.CopyToDataTable () como esperaba.

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

Pero cuando he cambiado alguna statement de código a continuación, no puedo llamar a CopyToDataTable (), ¿por qué?

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

En función de su uso de Field , los objetos en dataTable (que dataTable son del tipo Car ) heredan DataRow . Esto es necesario para llamar al método de extensión CopyToDataTable . Sin embargo, tal como está escrito, está devolviendo una enumeración de un tipo anónimo que no puede heredar DataRow .

Entonces, probablemente tu

 select new 

debiera ser

 select new Car 

para que devuelva un IEnumerable lugar de un IEnumerable<> de tipo anónimo.

Dependiendo de la estructura exacta de su clase de Car , podría ser necesario hacer algunos cambios sintácticos menores. Si Car tiene propiedades públicas, Make , Color y PetName , funcionará como sugerí. Si, en cambio, Car tiene un constructor con firma de método aproximadamente igual a

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

entonces tendrá que alterar la statement LINQ para ser

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

Puede construir su propia CopyToDataTable que tome cualquier tipo de IEnumerable (no solo DataRow ) y devuelva una nueva DataTable :

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

Aquí está la implementación (con la ayuda de MSDN ):

 public class ObjectShredder { private System.Reflection.FieldInfo[] _fi; private System.Reflection.PropertyInfo[] _pi; private System.Collections.Generic.Dictionary _ordinalMap; private System.Type _type; // ObjectShredder constructor. public ObjectShredder() { _type = typeof(T); _fi = _type.GetFields(); _pi = _type.GetProperties(); _ordinalMap = new Dictionary(); } ///  /// Loads a DataTable from a sequence of objects. ///  /// The sequence of objects to load into the DataTable. /// 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. /// Specifies how values from the source sequence will be applied to /// existing rows in the table. /// A DataTable created from the source sequence. public DataTable Shred(IEnumerable 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 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 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 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; } } 

Ahora puede agregar estas extensiones:

 public static class CustomLINQtoDataSetMethods { public static DataTable CopyToDataTable(this IEnumerable source) { return new ObjectShredder().Shred(source, null, null); } public static DataTable CopyToDataTable(this IEnumerable source, DataTable table, LoadOption? options) { return new ObjectShredder().Shred(source, table, options); } } 

Voilà! Ahora CopyToDataTable funciona con cualquier tipo de IEnumerable 🙂

Porque estás devolviendo un nuevo tipo anónimo, no el objeto del auto en sí.