假使应用LINQ重回的匿名对象实行数据绑定的话

  .NET中的LINQ对于操作集合对象提供了成都百货上千的有利,使得大家得以在C#中应用类似于SQL语句的点子对聚集中的对象进行查找、分组、总括等。使用LINQ写出来的代码简单明了,省去了笔者们本来必要采取多量for循环也许foreach循环才能兑现的效率。人人皆知,通过LINQ查询所重返的结果一般的话是三个以var所标识的匿名类型,该品种继承自IEnumerable接口,大家能够间接将它绑定到其余叁个数目绑定控件,如DropDownList,ListBox,DataGridView等。但这边有一个难点,对于DataGridView(WinForm版)来说,如果选取LINQ重临的匿名对象开始展览数量绑定的话,会失掉DataGridView中单击列标题举行多少排序的效益,那是因为DataGridView不能够从3个匿名对象中拿走到举行数量排序的现实性规则。要化解这一个标题,你能够给那几个匿名对象编排具体的排序算法,但是最简易的做法还是将以此匿名对象转换到大家所熟稔的聚众对象,如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方始就提供的1个效应吗?C#壮大方法。它同意我们向现有项目中“添加”方法,而无需创造新的派生类型、重新编写翻译或以其余办法修改原始类型。看看msdn上的牵线,先来谙习一下怎样是C#扩展方法吗!http://msdn.microsoft.com/zh-cn/library/bb383977.aspx

            // load sequence of scalars.

  C#推而广之方法是给现有项目“添加”二个办法,现有项目能够是主导数据类型(如int,string等),也足以是自定义类型。定义规则是扩充方法必须定义在一个即兴命名的静态类中,该方法必须是静态方法,可以Infiniti制命名,方法的参数列表必须以this关键字起始,首个即为要壮大的数据类型,第七个是三个变量名,同时参数列表中允许定义几个别的参数以落成格局的重载。来看1个事例。

            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接口扩充1个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从而获取点击列标题进行数量排序的功能。还有多少复杂一点的运用,给2个代码片段的截图。

 

图片 1

 

相关文章