在一个DataTable上进行LINQ查询

1142

我正在尝试在DataTable对象上执行LINQ查询,但奇怪的是我发现在DataTable上执行这样的查询并不简单。例如:

var results = from myRow in myDataTable
where results.Field("RowNo") == 1
select results;

这是不允许的。我该如何使类似的东西工作起来?

我很惊讶LINQ查询在DataTable上是不允许的!


3
你可以在以下网址中找到更多关于LINQ/Lambda的示例代码:http://webmingle.blogspot.com/2010_09_01_archive.html - user562221
这是因为datatables比LINQ早了一些年。使用强类型的datatable代替;这比这个弱类型、智能感知受挫的dt.Rows["FirstName]垃圾体验更好。使用强类型表格(在项目中添加一个DataSet类型文件并在可视化设计器中创建表格),你只需编写例如myStronglyTpedDataset.Person.Where(p => p.FirstName == "John") - 所有使其发生的魔法都已经完成。 - Caius Jard
@CaiusJard 这是否意味着您需要为每个查询创建 DataSet 类型的文件? - symbiont
并非每个查询都需要。数据集代表被建模的实体,因此在 EF 应用程序中可能会有一个 Order 实体,其中包含 Product 实体列表,您的强类型数据集具有具有与 ProductsDataTable 相关联的 OrdersDataTable。如果您有一个数据库,创建它们的工作量也相似,因为 EF 可以从现有数据库中生成实体,数据集设计器也可以。如果要向代码添加新实体,则使用数据集稍微容易一些;您只需单击添加表、单击添加列等,而 VS 在后台编写您的代码。 - Caius Jard
所以这里所需要的只是一个 AsEnumerable() 调用。不知道为什么需要那么多答案。 - Gert Arnold
22个回答

1408

由于DataRowCollection没有实现IEnumerable<T>,所以您无法对DataTableRows集合进行查询。 您需要使用DataTableAsEnumerable()扩展方法。像这样:

var results = from myRow in myDataTable.AsEnumerable()
where myRow.Field<int>("RowNo") == 1
select myRow;

正如@Keith所说,您需要添加对System.Data.DataSetExtensions的引用。

AsEnumerable()返回IEnumerable<DataRow>。如果您需要将IEnumerable<DataRow>转换为DataTable,请使用CopyToDataTable()扩展。

下面是一个使用Lambda表达式的查询示例:

var result = myDataTable
    .AsEnumerable()
    .Where(myRow => myRow.Field<int>("RowNo") == 1);

8
VB版本: Dim results = From myRow In myDataTable.AsEnumerable _ Where myRow.Field("RowNo") = 1 _ Select myRow - Jeff
17
我已经有提到该dll的参考文献,但是缺少了using System.Data; - Luke Duddridge
5
VB版本需要在myRow.Field和("RowNo")之间插入(Of String)。该部分应该写成:myRow.Field(Of String)("RowNo") = 1 - 参照 @Cros 的评论。 - yougotiger
8
这个解决方案过于复杂。像@JoelFan建议的那样,使用myDataTable.Rows即可。 - The Conspiracy
11
@Markus,只想澄清一下,@JoelFan的解决方案之所以适用于myDataTable.Rows,是因为myRow变量被明确转换为DataRow。在编译时,查询被重写为myDataTable.Rows.Cast<DataRow>().Where(myRow => (int)myRow["RowNo"] == 1)。就个人而言,我并不认为调用AsEnumerable()比调用Cast<DataRow>()更复杂。据我所知,它们的性能相同,所以这只是一个偏好问题。 - Collin K
显示剩余3条评论

141
var results = from DataRow myRow in myDataTable.Rows
    where (int)myRow["RowNo"] == 1
    select myRow

2
选择多行呢,而不仅仅是第一行? - Adjit
2
只需删除“where”行,您就可以获取所有行。 - JoelFan
2
是的,这就是我以前的做法,只不过用通用形式 myRow.Field<int>("RowNo") 替换了 (int)myRow["RowNo"] 以更方便地支持可空类型。 - Jonas

75

并不是因为DataTable故意不允许使用,而是因为DataTable在IQueryable和generic IEnumerable构造之前就已经存在了,而Linq查询需要这些接口的支持。

这两个接口都需要某种类型安全验证,而DataTable没有强类型。这也是为什么人们不能对ArrayList进行查询的原因。

要使Linq工作,您需要将结果映射到类型安全的对象上,并对其进行查询。


54

@ch00k所说:

using System.Data; //needed for the extension methods to work

...

var results = 
    from myRow in myDataTable.Rows 
    where myRow.Field<int>("RowNo") == 1 
    select myRow; //select the thing you want, not the collection

您还需要添加对System.Data.DataSetExtensions的项目引用。


2
如果您尝试这样做,您会发现它不起作用,除非您在myRow上放置特定类型或在Rows上使用Cast<DataRow>()。最好使用AsEnumerable() - NetMage
1
@NetMage 这个代码是在我发帖12年前有效的。只要你有 System.LinqSystem.Data.DataSetExtensions,那么 myDataTable.Rows 就会返回一个可枚举的 DataRow 集合。虽然这可能已经改变了,毕竟我已经十年没用它了。 - Keith
1
有趣 - 我猜它在某个时候被更改了,因为现在它在 .Net 或 .Net Core 上不起作用。 - NetMage
1
@NetMage 是的,我并不惊讶 DataSet 扩展没有被包含在 .NET Core 或 .NET Standard 中,因为当我回答这个问题时它们已经过时了。我真的不会在新项目中使用 DataSet,因为有更好的数据访问模型,无论是编码的简便性还是性能方面。 - Keith
2
它们在那里,但是 DataRowCollection 没有实现 IEnumerable<T>,只有 IEnumerable,因此无法与强类型的 LINQ 一起使用。 - NetMage

47

我知道这个问题已经被回答了几次,但是我想提供另一种方法:

我喜欢使用.Cast<T>()方法,它帮助我保持清晰,看到明确定义的类型,而且我认为.AsEnumerable()方法在深层调用它:

var results = from myRow in myDataTable.Rows.Cast<DataRow>() 
                  where myRow.Field<int>("RowNo") == 1 select myRow;
或者
var results = myDataTable.Rows.Cast<DataRow>()
                  .FirstOrDefault(x => x.Field<int>("RowNo") == 1);

正如评论中所指出的那样,不需要System.Data.DataSetExtensions或任何其他程序集(参考文献


5
不需要引用 System.Data.DataSetExtensions,即可正常运行。 - user423430

40
var query = from p in dt.AsEnumerable()
                    where p.Field<string>("code") == this.txtCat.Text
                    select new
                    {
                        name = p.Field<string>("name"),
                        age= p.Field<int>("age")                         
                    };

现在姓名和年龄字段是查询对象的一部分,可以像这样访问:Console.WriteLine(query.name);


1
我该如何使用名称?例如,MessageBox.Show(name) 是未定义的。 - user1372430

34

使用 LINQ 操作 DataSet/DataTable 数据

var results = from myRow in tblCurrentStock.AsEnumerable()
              where myRow.Field<string>("item_name").ToUpper().StartsWith(tbSearchItem.Text.ToUpper())
              select myRow;
DataView view = results.AsDataView();

1
AsDataView在我的Intellisense中没有出现。我已经包含了using System.Data.Linq和using System.Linq, 但仍然不起作用。你知道我错过了什么吗?提前感谢。 - Naomi
@Naomi 这来自于 System.Data.DataSetExtensions - Louis Waweru

30
//Create DataTable 
DataTable dt= new DataTable();
dt.Columns.AddRange(new DataColumn[]
{
   new DataColumn("ID",typeof(System.Int32)),
   new DataColumn("Name",typeof(System.String))

});

//Fill with data

dt.Rows.Add(new Object[]{1,"Test1"});
dt.Rows.Add(new Object[]{2,"Test2"});

//Now  Query DataTable with linq
//To work with linq it should required our source implement IEnumerable interface.
//But DataTable not Implement IEnumerable interface
//So we call DataTable Extension method  i.e AsEnumerable() this will return EnumerableRowCollection<DataRow>


// Now Query DataTable to find Row whoes ID=1

DataRow drow = dt.AsEnumerable().Where(p=>p.Field<Int32>(0)==1).FirstOrDefault();
 // 

25

试试这个简单的查询语句:

var result=myDataTable.AsEnumerable().Where(myRow => myRow.Field<int>("RowNo") == 1);

17

您可以像这样在行集合上使用LINQ to objects:

var results = from myRow in myDataTable.Rows where myRow.Field("RowNo") == 1 select myRow;

2
由于 DataTable.Rows 没有实现 IEnumerable,我无法看出这个查询如何编译。 - onedaywhen
@onedaywhen,我刚看到有些代码这样做了,而且它确实编译通过了。现在正试图弄清楚为什么。 - BVernon
或者您可以在Select方法中使用过滤器表达式:var results = myDataTable.Select("RowNo=1")。这将返回一个DataRow数组。 - Ishikawa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接