如何将LINQ查询到的结果由匿名类型var转换成DataTable对象

简介:

.NET中的LINQ对于操作集合对象提供了很多的便利,使得我们可以在C#中使用类似于SQL语句的方式对集合中的对象进行查找、分组、统计等。使用LINQ写出来的代码简单明了,省去了我们原本需要使用大量for循环或者foreach循环才能实现的效果。众所周知,通过LINQ查询所返回的结果一般来说是一个以var所标识的匿名类型,该类型继承自IEnumerable接口,我们可以直接将它绑定到任何一个数据绑定控件,如DropDownList,ListBox,DataGridView等。但这里有一个问题,对于DataGridView(WinForm版)来说,如果使用LINQ返回的匿名对象进行数据绑定的话,会失去DataGridView中单击列标题进行数据排序的功能,这是因为DataGridView不能从一个匿名对象中获取到进行数据排序的具体规则。要解决这个问题,你可以给这个匿名对象编写具体的排序算法,不过最简单的做法还是将这个匿名对象转换成我们所熟悉的集合对象,如DataTable,然后再绑定到DataGridView中。

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

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

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

DataTable orders 
=  ds.Tables[ " SalesOrderHeader " ];

//  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;

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

//  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;
复制代码

  不过这个方法不是我们所希望的!原因是其中的泛型类型必须是DataRow而不能是自定义类型。怎么办呢?我们可不可以将这个方法修改一下让它能支持任意类型?

  还记得从.NET 3.0开始就提供的一个功能吗?C#扩展方法。它允许我们向现有类型中“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。看看msdn上的介绍,先来熟悉一下什么是C#扩展方法吧!http://msdn.microsoft.com/zh-cn/library/bb383977.aspx

  C#扩展方法是给现有类型“添加”一个方法,现有类型可以是基本数据类型(如int,string等),也可以是自定义类型。定义规则是扩展方法必须定义在一个任意命名的静态类中,该方法必须是静态方法,可以任意命名,方法的参数列表必须以this关键字开始,第二个即为要扩展的数据类型,第三个是一个变量名,同时参数列表中允许定义多个其它参数以实现方法的重载。来看一个例子。

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

  静态类MyExtensions被定义在命名空间ExtensionMethods中,静态方法WordCount的参数列表中规定了该方法是对String类型的方法进行了扩展。在实际应用中,你需要在代码中添加对ExtensionMethods命名空间的引用,然后通过String.WordCount()的方式来调用这个扩展方法。是不是很神奇啊?再看一个例子。

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

  上面的代码片段对DirectoryInfo类的方法进行了扩展,将上述代码补充完整,便可以直接通过下面的方式调用新扩展的方法。

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

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

复制代码
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   static   string  TestMethod( this  Student s,  string  name)
        {
            
return  s.Description(name);
        }
    }
}
复制代码

  于是,自定义的Student类具有了包含一个重载的TestMethod方法,该方法允许接收一个string类型的参数或者没有参数。

  好了!回到我们的主题上来。既然C#扩展方法允许我们对类型添加方法,那么我们完全可以对已有的IEnumerable接口扩展一个CopyToDataTable方法,使其可以将LINQ返回的var匿名类型转换成DataTable。来看下具体的实现。
复制代码
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;

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

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


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

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

            
this .dataGridView1.DataSource  =  table1;
        }
    }

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

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

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

    
public   static   class  DataSetLinqOperators
    {
        
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);
        }

    }

    
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从而获取点击列标题进行数据排序的功能。还有稍微复杂一点的应用,给一个代码片段的截图。

 

 


本文转自Jaxu博客园博客,原文链接:http://www.cnblogs.com/jaxu/archive/2011/08/02/2125055.html,如需转载请自行联系原作者


相关文章
|
6月前
|
存储 开发框架 .NET
C#中将DataTable转化成ListT的方法解析
C#中将DataTable转化成ListT的方法解析
517 0
|
存储 SQL 开发框架
Linq To Object标准操作符最强总结
Linq To Object标准操作符最强总结
138 0
|
JSON JavaScript 前端开发
DataTable 转换成 Json的3种方法
在web开发中,我们可能会有这样的需求,为了便于前台的JS的处理,我们需要将查询出的数据源格式比如:List、DataTable转换为Json格式。特别在使用Extjs框架的时候,Ajax异步请求的数据格式就是Json。
1489 0
|
SQL .NET C#
用LINQ在集合中查询特定对象
这里是原文出处: 简单的概括LINQ LINQ是Language-Integrated Query的缩写,是C# 3.0和VB 9.0中新加入的语言特性,可以在编程时使用内置的查询语言进行基于集合的操作。
855 0
|
.NET 开发框架