【问题标题】:List<T> joins DataTableList<T> 连接数据表
【发布时间】:2021-01-25 06:50:53
【问题描述】:

我有一个对象列表 (lst) 和数据表 (dt)。我想在公共字段(代码为字符串)上加入 lst 和 dt,并且需要返回 lst 中的所有匹配行。

我的列表包含两列,即代码和名称以及以下值:

code name
==== ====    
1    x    
2    y    
3    z

DataTable 包含两列,即代码和值以及以下值:

code value
==== =====    
3    a    
4    b    
5    c

结果是:

3   z

下面是我的代码;但我知道这不是一个正确的说法,因此在这里寻求您的建议。如果您能指导我如何编写正确的陈述,我将不胜感激。

var ld = from l in lst
         join d in dt.AsEnumerable() on l.code equals d.code
         select new { l.code, l.name };

【问题讨论】:

  • equals d.Field&lt;int&gt;("code")

标签: c# linq model-view-controller datatable


【解决方案1】:

您可以使用 Linq 查询或 Join 扩展方法来加入代码集合。只是当您从数据表中选择数据时,您需要使用 dt.Field 方法。请使用以下任一代码。

查询1:

var ld = lst.Join(dt.AsEnumerable(),
            l => l.code,
            d => d.Field<string>("code"),
            (l, d) => new
            {
                l.code,
                l.name,
                value = d.Field<string>("value")
            }).ToList();

查询2:

var ld = (from l in lst
          join d in dt.AsEnumerable()
          on l.code equals d.Field<string>("code")
          select new
          {
              l.code,
              l.name,
              value = d.Field<string>("value")
          }).ToList();

查询3:

var ld = (from l in lst
          join d in dt.AsEnumerable()
          on l.code equals d.Field<string>("code")
          let value = d.Field<string>("value")
          select new
          {
              l.code,
              l.name,
              value
          }).ToList();

【讨论】:

  • 亲爱的 Ayaz,您的解决方案完美无缺。非常感谢你的帮助。问题,为什么当我们选择 new { l.code, l.name, d.Field("value") } 时它无法识别,并且仅在分配 d=Field("value") 时才接受然后执行 select new { l.code, l.name, value = d.Field("value") } 代替?
  • 它确实识别 d.Field("value") 但它抱怨属性名称。由于匿名类型是从 select new { ..... } 返回的,因此它需要定义属性名称。对于其他两个代码和名称,它就这样。为 code 和 name 定义属性名称是可选的,但 value 被评估为表达式,因此我们需要显式设置属性名称。
  • 您可以将 d.Field("value") 移出匿名类型并直接使用 value。请检查答案。我添加了 Query#3。
  • 感谢您回答我的问题——非常感谢
【解决方案2】:

您可以尝试以下任何一种方法。

var ld = from l in lst
     join d in dt.AsEnumerable() on l.code equals d.Field<int>("code")
     select new { l.code, l.name };

var ld = lst.Join(dt.AsEnumerable(), l => l.code, d => d.Field<int>("code"), (l,d) => new { l.code, l.name });

【讨论】:

  • 亲爱的 Sarwar,感谢您提供解决方案。我尝试运行您的代码并打印查询结果,它返回如下: 1 : x 2 : y 3 : z 它应该打印 3 : z
  • List lst = new List(); lst.Add(new CodeName { code = "1", name = "x" }); lst.Add(new CodeName { code = "2", name = "y" }); lst.Add(new CodeName { code = "3", name = "z" });数据表 dt = 新数据表(); dt.Columns.Add("code", typeof(string)); dt.Columns.Add("值", typeof(string)); dt.Rows.Add("1", "a"); dt.Rows.Add("2", "b"); dt.Rows.Add("3", "c");
  • var ld = from l in lst join d in dt.AsEnumerable() on l.code equals d.Field("code") select new { l.code, l.name } ;
  • 看来,您没有在 DataTable 中添加正确的数据。在问题中,您在列表中提到代码为 1,2,3,在 DataTable 中提到代码为 3,4,5。但在评论中,您尝试在列表和 DataTable 中添加 1、2、3,因此您得到 1:x 2:y 3:z。请按如下方式填充数据表并尝试。数据表 dt = 新数据表(); dt.Columns.Add("code", typeof(string)); dt.Columns.Add("值", typeof(string)); dt.Rows.Add("3", "a"); dt.Rows.Add("4", "b"); dt.Rows.Add("5", "c");
  • Sarwar,你看对了。非常感谢您的建议和帮助。最后一件事,我想根据 Peter 的建议扩展我的选择以显示 d 中的项目,例如 select new { l.code, l.name, d.value };但 IDE 智能无法识别它
【解决方案3】:

不清楚您需要的输出是什么,但看起来您正确获取了唯一的公共记录。您可以将select 扩展到

select new { l.code, l.name, d.value }

这将提供两个表中的所有数据/列。

code name value
==== ==== =====
  3    z     a

【讨论】:

  • 嗨彼得,上面的查询结果显示为: 1 : x 2 : y 3 : z
【解决方案4】:

试试这个:

var ld = from l in lst
         join d in dt.Cast <DataRow>() on l.code equals d["code"].ToString()
         select new { l.code, l.name };

【讨论】:

  • 嗨 Moha,智能投诉说 DataTable 不包含“Cast”的定义,并且最佳扩展方法过载“EnumerableRowCollectionExtensions.Cast(EnumerableRowCollection)”需要“EnumerableRowCollection”类型的接收器'
【解决方案5】:

所以你有一个列表和一个数据表。您不打算使用数据表的值,只使用代码。

您想保留那些列表项,它们的代码也是 DataTable 中的代码。

如果您打算将 DataTable 用于解决此问题以外的其他事情,我的建议是首先创建一个过程,将您的 DataTable 转换为可枚举的序列。

这样你可以添加 LINQ 语句,不仅针对这个问题,还针对其他问题。

让我们为您的 DataTable 创建一个扩展方法,将数据转换为 DataTable 中的项目。见extension methods demystified.

唉,我不知道你的 DataTable 里有什么,假设你的 DataTable 包含 Orders

class CustomerOrder
{
    public int Id {get; set;}
    public int CustomerId {get; set;}

    public int Code {get; set;}
    public string Value {get; set;} 
    ...
}

扩展类DataTable功能的扩展方法:

public static IEnumerable<Order> ToCustomerOrders(this DataTable table)
{
    return table.AsEnumerable().Select(row => new CustomerOrder
    {
        Id = ...
        CustomerId = ...
        Code = ...
        Value = ...
     };
}

我对 DataTables 不是很熟悉,但你知道如何将行中的单元格转换为正确的值。

用法:

DataTable table = ...
Int customerId = 14;

var ordersOfThisCustomer = table.ToCustomerOrders
    .Where(customerOrder => customerOrder.CustomerId == customerId)
    .FirstOrDefault();

换句话说:将数据表逐行转换为 CustomerOrders,并检查每个转换后的 CustomerOrder 是否具有等于 14 的 CustomerId。如果找到则停止。如果没有这样的行,则返回 null。

现在您已经有了一个很好的可重复使用的过程,而且易于测试、调试和更改,我们可以回答您的问题。

给定一个包含 CustomerOrders 的 DataTable,以及一个包含 CodeName 的项目序列,只保留序列中的那些项目,这些项目的代码也是 DataTable 中的代码。

var dataTable = ...   // your DataTable, filled with CustomerOrders.
var codeNames = ...   // your list with Codes and Names

var codesInDataTable = dataTable.ToCustomerOrders
    .Select(customerOrder => customerOrder.Code)
    .Distinct();

这将创建一个可枚举序列,该序列将逐行转换您的 DataTable 并提取属性代码。重复的代码值将被删除。

如果代码是唯一的,则不需要 Distinct。

注意:可枚举序列尚未枚举!

var result = codeNames
    .Where(codeName => codesInDataTable.Contains(codeName.Code))
    .ToList();

简而言之:对于列表中的每个 [Code, Name] 组合,只保留那些 Code 的值也在 codesInDataTable 中的 [Code, Name] 组合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多