【问题标题】:LINQ to Entities query to DataTableLINQ to Entities 查询到 DataTable
【发布时间】:2013-08-25 23:02:27
【问题描述】:

我需要将从此 LINQ to Entities 查询(如下)返回的数据存储到 DataTable 中,以便我可以将其用作 DataGridView 的数据源,我该怎么做?

在这种情况下,我使用 LINQ to Entities 来查询实体框架概念模型,因此 db 是一个继承自 System.Data.Entity.DbContext 的类。

using (TccContext db = new TccContext())
{
    var query = from vendedor in db.Vendedores.AsEnumerable()
                where vendedor.codigo == Convert.ToInt32(textBoxPesquisa.Text)
                select vendedor;
    // I'd like to do something like DataTable dt = query;
}

我尝试过这样做(如下),但它在执行期间抛出异常 [1]。

using (TccContext db = new TccContext())
{
    IEnumerable<DataRow> query = (IEnumerable<DataRow>)(from vendedor in db.Vendedores.AsEnumerable()
                                                        where vendedor.codigo == Convert.ToInt32(textBoxPesquisa.Text)
                                                        select vendedor);

    using (DataTable dt = query.CopyToDataTable<DataRow>())
    {
        this.dataGridViewProcura.Rows.Add(
            dt.Rows[0][0],  // Código
            dt.Rows[0][1],  // Nome
            dt.Rows[0][2]); // Venda Mensal
    }
}

[1]:例外:InvalidCastException

Unable to cast object of type 'WhereEnumerableIterator`1[Projeto_TCC.Models.Vendedor]' to type 'System.Collections.Generic.IEnumerable`1[System.Data.DataRow]'.

提前致谢

【问题讨论】:

    标签: c# linq entity-framework datagridview datatable


    【解决方案1】:

    这里有一件重要的事情,当您选择 vendedor 时,您将 Linq 查询转换为 (IEnumerable&lt;DataRow&gt;),因此我假设供应商是 Vendedor 的一个实例,因此您的查询将返回一个IEnumerable&lt;Vendedor&gt;

    这应该可以解决您的问题,但是,您可以尝试使用生成的 DataTable 作为 DataGridView 的 DataSource 吗?应该是这样的:

    var query = (from vendedor in db.Vendedores.AsEnumerable()
                       where vendedor.codigo == Convert.ToInt32(textBoxPesquisa.Text)
                       select vendedor);
    var dt = query.CopyToDataTable<Vendedor>();
    this.dataGridViewProcura.DataSource = dt;
    

    希望我能帮上忙!

    编辑

    作为一个侧面(非常个人)的说明,您可以尝试在您的选择中使用 lambda,它们看起来更漂亮:)

    var pesquisa = Convert.ToInt32(textBoxPesquisa.Text);
    var query = db.Vendedores.Where(vendedor => vendedor.codigo == pesquisa);
    
    var dt = query.CopyToDataTable<Vendedor>();
    this.dataGridViewProcura.DataSource = dt;
    

    干净多了,你不觉得吗?

    编辑 2 我刚刚意识到您在 CopyToDataTable 上所说的仅适用于 DataRow,所以最后一个(诚然不是那么干净)的解决方案是模仿助手上的逻辑?

    public DataTable CopyGenericToDataTable<T>(this IEnumerable<T> items)
    {
        var properties = typeof(T).GetProperties();
        var result = new DataTable();
    
        //Build the columns
        foreach ( var prop in properties ) {
            result.Columns.Add(prop.Name, prop.PropertyType);
        }
    
        //Fill the DataTable
        foreach( var item in items ){
            var row = result.NewRow();
    
            foreach ( var prop in properties ) {
                var itemValue = prop.GetValue(item, new object[] {});
                row[prop.Name] = itemValue;
            }
    
            result.Rows.Add(row);
        }
    
        return result;
    }
    

    现在,需要考虑的事项:

    • 此解决方案不会适用于复杂的属性
    • 自定义生成的表格可能有点棘手

    虽然这可能会解决问题,但我认为这不是一个很好的方法,但它可能是一个不错的想法的开始:)

    希望这次能帮上忙!

    【讨论】:

    • 如果我这样使用,query 中的 CopyToDataTable 方法将不可用。请注意,我使用的是实体框架。
    • 我在最后一个来源上打错了,正如编辑所说,CopyToDataTable 方法应该在那里工作。顺便说一句 - 答案有帮助吗?
    • 正如您在同一链接中看到的那样,要访问 CopyToDataTable 方法,IEnumerable 中的 T 类型必须是 DataRow 类型,但您使用的是表示数据库的类表供应商我最终无法访问包含 CopyToDataTable 的那些扩展方法。
    • 转换为数据表无效的解决方案
    • 我一直在为 DataTable 解决方案实施其他几个对象“粉碎机”,例如您在 EDIT 2 (@David Conde) 中建议的解决方案,但我发现这对于复杂的类型(如前所述),尤其是具有外键或非常昂贵或两者兼而有之的 EntityType。我认为在许多情况下,最好放弃实体类型并直接查询数据库,这很容易转换为 DataTable
    【解决方案2】:

    这是 MSDN 推荐的解决方案:https://msdn.microsoft.com/en-us/library/bb669096(v=vs.110).aspx

    我已经成功实现了 (*为处理可为空的 DateTime 进行了少量添加。) 如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Data;
    using System.Reflection;
    
    
    /// <summary>
    /// Converts Entity Type to DataTable
    /// </summary>
    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[p.Name];
                    //Added Try Catch to account for Nullable Types  
                    try
                    {
                        dc = table.Columns.Contains(p.Name) ? table.Columns[p.Name]
                        : table.Columns.Add(p.Name, p.PropertyType);
                    }
                    catch (NotSupportedException nsEx)
                    {
                        string pType = p.PropertyType.ToString();
                        dc = pType.Contains("System.DateTime") ? table.Columns.Add(p.Name, typeof(System.DateTime)) : table.Columns.Add(p.Name);
                        //dc = table.Columns.Add(p.Name); //Modified to above statment in order to accomodate Nullable date Time 
                    }
    
    
    
    
                    // Add the property to the ordinal map.
                    _ordinalMap.Add(p.Name, dc.Ordinal);
                }
            }
    
            // Return the table.
            return table;
        }
    }
    

    }

    此解决方案的(大)警告是它**昂贵**并且您必须在错误处理中对其进行自定义。

    【讨论】:

      【解决方案3】:

      你可以放

      var query = from ....

      this.dataGridViewProcura.DataSource = query.tolist()

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多