C# 使用带有“new {..}”LINQ 查询的 CopyToDataTable 的异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1072120/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 07:33:55  来源:igfitidea点击:

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

c#linqdatatable

提问by Anonymous

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

从这段代码中,我可以按预期调用 bmwCars.CopyToDataTable()。

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

But when I have change some statement of code to below, I can't call CopyToDataTable(), why?

但是当我将一些代码语句更改为下面时,我无法调用 CopyToDataTable(),为什么?

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

采纳答案by jason

Based on your use of Field<T>, the objects in dataTable(which I am assuming are of type Car) inherit DataRow. This is necessary to call the CopyToDataTableextension method. As written, however, you are returning an enumeration of an anonymous type which can not inherit DataRow.

根据您对 的使用Field<T>,其中的对象dataTable(我假设是 类型Car)继承DataRow. 这是调用CopyToDataTable扩展方法所必需的。但是,正如所写的那样,您将返回一个无法继承的匿名类型的枚举DataRow

So, probably your

所以,可能你的

select new

should be

应该

select new Car

so that you're returning an IEnumerable<Car>instead of an IEnumerable<>of anonymous type.

以便您返回一个IEnumerable<Car>而不是IEnumerable<>匿名类型。

Depending on the exact structure of your Carclass, it might be necessary to make some minor syntatical changes. If Carhas public properties, Make, Color, and PetNamethen it will work as I suggested. If, instead, Carhas a constructor with method signature approximately equal to

根据Car类的确切结构,可能需要进行一些小的语法更改。如果Car有公共属性,Make, ColorPetName那么它会按照我的建议工作。相反,如果Car有一个方法签名近似等于的构造函数

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

then you will have to alter the LINQ statement to be

那么您必须将 LINQ 语句更改为

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

回答by MiffTheFox

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

因为您要返回一个新的匿名类型,而不是汽车对象本身。

回答by Tim Schmelter

You could build your own CopyToDataTablethat takes any kind of IEnumerable(not only DataRow)and returns a new DataTable:

您可以构建自己的CopyToDataTable,它接受任何类型的 IEnumerable(不仅是DataRow)并返回一个新的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 (with help of MSDN):

这是实现(在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);
    }  
}

Voilà! Now CopyToDataTableworks with any kind of IEnumerable<T>:)

瞧!现在CopyToDataTable适用于任何类型的IEnumerable<T>:)