Exception using CopyToDataTable with the "new {..}" LINQ query

From this code, I can call bmwCars.CopyToDataTable (), as expected.

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

But when I have some kind of code instruction below, I can't call CopyToDataTable (), why?

 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") }; 
+3
c # linq datatable
source share
3 answers

Based on the use of Field<T> , the objects in dataTable (which I assume are of type Car ) inherit DataRow . This is necessary to call the CopyToDataTable extension CopyToDataTable . However, as written, you are returning an enumeration of an anonymous type that DataRow cannot inherit.

So maybe your

 select new 

it should be

 select new Car 

so that you return an IEnumerable<Car> instead of an IEnumerable<> an anonymous type.

Depending on the exact structure of your Car class, you may need to make some minor syntax changes. If Car has public properties, Make , Color and PetName , then it will work, as I suggested. If, instead, Car has a constructor with a method signature approximately equal to

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

you will have to change the LINQ statement as

 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") ); 
+5
source share

You can create your own CopyToDataTable , which accepts any type of IEnumerable (not just DataRow ) and returns a new DataTable :

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

Here is the implementation (using 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; } } 

Now you can add these extensions:

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

Voila! Now CopyToDataTable works with any type of IEnumerable<T> :)

+7
source share

Because you are returning a new anonymous type, not the car object itself.

+3
source share

All Articles