假若下LINQ返回的匿名对象进行数据绑定的口舌。

如果使用LINQ返回的匿名对象进行数据绑定的话

  .NET中的LINQ对于操作集合对象提供了很多的便宜,使得我们得以在C#遭行使类于SQL语句的章程对聚集中之目标进行查找、分组、统计等。使用LINQ写出来的代码简单明了,省去了俺们原来欲动用大量for循环或者foreach循环才会兑现之法力。众所周知,通过LINQ查询所返的结果一般的话是一个为var所标识的匿名类型,该类型继承自IEnumerable接口,我们可一直将它们绑定到其他一个数据绑定控件,如DropDownList,ListBox,DataGridView等。但此间来一个题目,对于DataGridView(WinForm版)来说,如果以LINQ返回的匿名对象开展多少绑定的话,会去DataGridView中单击列标题进行数量排序的效益,这是坐DataGridView不可知于一个匿名对象吃落到开展多少排序的有血有肉规则。要缓解是题材,你可以于这匿名对象编排具体的排序算法,不过最简易的做法还是用这匿名对象转换成为我们所熟悉的聚合对象,如DataTable,然后再次绑定到DataGridView中。

class Sample
    {
        static void Main(string[] args)
        {
            // create sequence 
            Item[] items = new Item[] { new Book{Id = 1, Price = 13.50, Genre = “Comedy”, Author = “Jim Bob”}, 
                                        new Book{Id = 2, Price = 8.50, Genre = “Drama”, Author = “John Fox”},  
                                        new Movie{Id = 1, Price = 22.99, Genre = “Comedy”, Director = “Phil Funk”},
                                        new Movie{Id = 1, Price = 13.40, Genre = “Action”, Director = “Eddie Jones”}};

  查找msdn,你得找到以LINQ to
DataSet的结果转换成DataTable的方法。下面的代码有来源于msdn上之牵线,http://msdn.microsoft.com/zh-cn/library/bb396189(v=vs.90).aspx

                        
            var query1 = from i in items
                         where i.Price > 9.99
                         orderby i.Price
                         select i;

// Bind the System.Windows.Forms.DataGridView object
// to the System.Windows.Forms.BindingSource object.
dataGridView.DataSource = bindingSource;

            // load into new DataTable
            DataTable table1 = query1.CopyToDataTable();

// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

            // load into existing DataTable – schemas match            
            DataTable table2 = new DataTable();
            table2.Columns.Add(“Price”, typeof(int));
            table2.Columns.Add(“Genre”, typeof(string));

DataTable orders = ds.Tables[“SalesOrderHeader”];

            var query2 = from i in items
                         where i.Price > 9.99
                         orderby i.Price
                         select new {i.Price, i.Genre};

// Query the SalesOrderHeader table for orders placed 
// after August 8, 2001.
IEnumerable<DataRow> query =
    from order in orders.AsEnumerable()
    where order.Field<DateTime>(“OrderDate”) > new DateTime(2001, 8, 1)
    select order;

            query2.CopyToDataTable(table2, LoadOption.PreserveChanges);

// Create a table from the query.
DataTable boundTable = query.CopyToDataTable<DataRow>();

            // load into existing DataTable – expand schema + autogenerate new Id.
            DataTable table3 = new DataTable();
            DataColumn dc = table3.Columns.Add(“NewId”, typeof(int));
            dc.AutoIncrement = true;
            table3.Columns.Add(“ExtraColumn”, typeof(string));

// Bind the table to a System.Windows.Forms.BindingSource object, 
// which acts as a proxy for a System.Windows.Forms.DataGridView object.
bindingSource.DataSource = boundTable;

            var query3 = from i in items
                         where i.Price > 9.99
                         orderby i.Price
                         select new { i.Price, i.Genre };

  不过这个法无是我们所希望的!原因是里的泛型类型必须是DataRow而休克是自从定义类型。怎么收拾呢?我们只是免得以用之主意修改一下吃它们亦可支撑任意档次?

            query3.CopyToDataTable(table3, LoadOption.PreserveChanges);

  还记从.NET
3.0始就提供的一个意义吗?C#扩展方法。它同意我们为现有项目受到“添加”方法,而无论是需创建新的派生类型、重新编译或因任何方法修改原始类型。看看msdn上之牵线,先来熟悉一下哟是C#恢宏方法吧!http://msdn.microsoft.com/zh-cn/library/bb383977.aspx

            // load sequence of scalars.

  C#扩张方法是被现有项目“添加”一个艺术,现有项目可以是中心数据类(如int,string等),也得以是由定义类型。定义规则是扩大方法必须定义在一个随机命名的静态类吃,该办法要是静态方法,可以随意命名,方法的参数列表必须坐this关键字开头,第二个就为要扩大的数据类型,第三只是一个变量号称,同时参数列表中允许定义多单其他参数为贯彻方式的重载。来拘禁一个例子。

            var query4 = from i in items
                         where i.Price > 9.99
                         orderby i.Price
                         select i.Price;

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static int WordCount(this String str)
        {
            return str.Split(new char[] { ‘ ‘, ‘.’, ‘?’ }, 
                             StringSplitOptions.RemoveEmptyEntries).Length;
        }
    }   
}

            var DataTable4 = query4.CopyToDataTable();
        }

  静态类MyExtensions被定义在命名空间ExtensionMethods中,静态方法WordCount的参数列表中确定了拖欠方式是对String类型的法门开展了扩大。在骨子里使用被,你用以代码中加上对ExtensionMethods命名空间的援,然后经过String.WordCount()的计来调用这个扩展方法。是休是异常神奇啊?再拘留一个例证。

        public class Item
        {
            public int Id { get; set; }
            public double Price { get; set; }
            public string Genre { get; set; }   
        }

namespace MyExtension
{
    public static class Test    {
        public static XElement ToXml(this DirectoryInfo dir)
        {
            // TO Do Something
        }
    } 
}

        public class Book : Item
        {
            public string Author { get; set; }
        }

  上面的代码有对DirectoryInfo类的办法开展了扩大,将上述代码补充完整,便可以一直通过下面的章程调用新扩张的方。

        public class Movie : Item
        {
            public string Director { get; set; }
        }
        
    }

DirectoryInfo dir = new DirectoryInfo(path);
dir.ToXml();

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

  C#扩展方法允许对从定义的门类进行扩张,同时同意带参数,支持重载。看下面的例子。

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

namespace TestExtendMethod
{
    public class Student
    {
        public string Description()
        {
            return “Student………….”;
        }
        public string Description(string name)
        {
            return “the student’s name is ” + name;
        }
    }

    }

    public static class Extensions
    {
        public static string TestMethod(this Student s)
        {
            return s.Description();
        }

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

        public static string TestMethod(this Student s, string name)
        {
            return s.Description(name);
        }
    }
}

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

  于是,自定义的Student类具有了涵盖一个重载的TestMethod方法,该办法允许收取一个string类型的参数或者尚未参数。

        public DataTable Shred(IEnumerable<T> source, DataTable table, LoadOption? options)
        {
            if (typeof(T).IsPrimitive)
            {
                return ShredPrimitive(source, table, options);   
            }
    

  好了!回到我们的主题上来。既然C#壮大方法允许我们本着品种丰富方法,那么我们完全好对曾经有些IEnumerable接口扩展一个CopyToDataTable方法,使该得以用LINQ返回的var匿名类型转换成DataTable。来拘禁下实际的落实。

            if (table == null)
            {
                table = new DataTable(typeof(T).Name);
            }

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

            // now see if need to extend datatable base on the type T + build ordinal map
            table = ExtendTable(table, typeof(T));

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

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

            // create sequence 
            Item[] items = new Item[] { new Book{Id = 1, Price = 13.50, Genre = “Comedy”, Author = “Jim Bob”}, 
                                        new Book{Id = 2, Price = 8.50, Genre = “Drama”, Author = “John Fox”},  
                                        new Movie{Id = 1, Price = 22.99, Genre = “Comedy”, Director = “Phil Funk”},
                                        new Movie{Id = 1, Price = 13.40, Genre = “Action”, Director = “Eddie Jones”}};

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

            var query1 = from i in items
                         where i.Price > 9.99
                         orderby i.Price
                         select i;

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

            // load into new DataTable
            DataTable table1 = query1.CopyToDataTable();

            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;

            this.dataGridView1.DataSource = table1;
        }
    }

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

    public class Item
    {
        public int Id { get; set; }
        public double Price { get; set; }
        public string Genre { get; set; }
    }

        public DataTable ExtendTable(DataTable table, Type type)
        {
            // value is type derived from T, may need to extend table.
            foreach (FieldInfo f in type.GetFields())
            {
                if (!_ordinalMap.ContainsKey(f.Name))
                {
                    DataColumn dc = table.Columns.Contains(f.Name) ? table.Columns[f.Name]
                        : table.Columns.Add(f.Name, f.FieldType);
                    _ordinalMap.Add(f.Name, dc.Ordinal);               
                }
            }
            foreach (PropertyInfo p in type.GetProperties())
            {
                if (!_ordinalMap.ContainsKey(p.Name))
                {
                    DataColumn dc = table.Columns.Contains(p.Name) ? table.Columns[p.Name]
                        : table.Columns.Add(p.Name, p.PropertyType);
                    _ordinalMap.Add(p.Name, dc.Ordinal);
                }
            }
            return table;
        }

    public class Book : Item
    {
        public string Author { get; set; }
    }

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

    public class Movie : Item
    {
        public string Director { get; set; }
    }

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

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

            if (instance.GetType() != typeof(T))
            {
                ExtendTable(table, instance.GetType());
                fi = instance.GetType().GetFields();
                pi = instance.GetType().GetProperties();
            }

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

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

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

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

        public DataTable Shred(IEnumerable<T> source, DataTable table, LoadOption? options)
        {
            if (typeof(T).IsPrimitive)
            {
                return ShredPrimitive(source, table, options);
            }

            if (table == null)
            {
                table = new DataTable(typeof(T).Name);
            }

            // now see if need to extend datatable base on the type T + build ordinal map
            table = ExtendTable(table, typeof(T));

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

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

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

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

        public DataTable ExtendTable(DataTable table, Type type)
        {
            // value is type derived from T, may need to extend table.
            foreach (FieldInfo f in type.GetFields())
            {
                if (!_ordinalMap.ContainsKey(f.Name))
                {
                    DataColumn dc = table.Columns.Contains(f.Name) ? table.Columns[f.Name]
                        : table.Columns.Add(f.Name, f.FieldType);
                    _ordinalMap.Add(f.Name, dc.Ordinal);
                }
            }
            foreach (PropertyInfo p in type.GetProperties())
            {
                if (!_ordinalMap.ContainsKey(p.Name))
                {
                    DataColumn dc = table.Columns.Contains(p.Name) ? table.Columns[p.Name]
                        : table.Columns.Add(p.Name, p.PropertyType);
                    _ordinalMap.Add(p.Name, dc.Ordinal);
                }
            }
            return table;
        }

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

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

            if (instance.GetType() != typeof(T))
            {
                ExtendTable(table, instance.GetType());
                fi = instance.GetType().GetFields();
                pi = instance.GetType().GetProperties();
            }

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

  
Item,Book,Movie都是从定义类型,扩展方法对IEnumerable泛型接口添加了力所能及支撑任意档次并回到DataTable的方法CopyToDataTable,于是,我们得以直接对LINQ返回的var匿名类型应用CopyDoDataTable方法并以返回值赋值给DataTable对象。然后用DataTable直接绑定给DataGridView从而获取点击列标题进行数量排序的职能。还有多少复杂一点底运用,给一个代码有的截图。

 

图片 1