【问题标题】:Converting Datatable to sublist using linq - retrieve rows without group by key使用 linq 将 Datatable 转换为子列表 - 检索没有分组的行
【发布时间】:2015-04-09 10:59:13
【问题描述】:

我需要一些帮助来解决这个问题。 我正在尝试将我的数据表转换为分层子列表,然后使用 LINQ 转换为 json。下面的代码运行完美。

var result = rows
    .GroupBy(x => new {b = x["Name"], a = x["Id"], c = x["MorningShift"], d = x["EveningShift"]})
    .OrderBy(a => a.Key.b)
    .Select(empl => new
    {
        Id = empl.Key.a,
        Name = empl.Key.b,
        MorningShift = empl.Key.c,
        EveningShift = empl.Key.d,
        Details = empl
            .Select(z => new
            {
                RouteId=z["RouteId"],
                ReportingHRName = z["ReportingHRName"],
                ShiftType = z["ShiftType"]
            })
            .OrderBy(a => a.ReportingHRName)

    });

员工可以上早班或晚班或两者兼有。我需要员工的详细信息作为子列表。我认为我的 LINQ 知识还不够好。我已经按 Name、Id、MorningShift、EveningShift 分组以使用该键检索这些列。

但是这样做,如果员工的早班为空,那么将有单独的列表 - 具有空值和不具有空值。 (当 GroupBy 完成时) 这是不希望的。

所以最后,我需要一些帮助来在不使用键的情况下检索列-morningshift 和 nightshift。

注意:如果我尝试这样做

EveningShift = x["EveningShift"],
MorningShift = x["MorningShift"]

那么我无法选择每个员工的 ReportingHRName 和 ShiftType 作为子列表。

我收到了类似的帖子 (Convert Hierarchical DataTable to Json)。但这无法回答我的问题。

编辑的数据表示例:

 Id   Name   MorningShift    EveningShift  RouteID ReportingHRName ShiftType
 1   abc       9               null         11      alex           full time
 2   def       8               null         12      robin          part time
 3   geh       null            5            13      shek           full time
 4   def       null            6            13      shek           part time

JSON 列表示例

    [{"Id":"1","Name":"abc","MorningShift":"9","EveningShift":"null","Details"[{"RouteId":"11","ReportingHRName":"alex","ShiftType":"full time"}]},{"Id":"2","Name":"def","MorningShift":"8","EveningShift":"6","Details":[{"RouteId":"12","ReportingHRName":"robin","ShiftType":"part time"}{"RouteId":"13","ReportingHRName":"shek","ShiftType":"full time"}]}{"Id":"3","Name":"geh","MorningShift":"null","EveningShift":"5","Details"[{"RouteId":"13","ReportingHRName":"shek","ShiftType":"full time"}]}]

因此,MorningShift 和 EveningShift 应该在没有 groupby 员工姓名(姓名)的情况下检索。但 RouteId、ReportingHRName 和 ShiftType 应按员工分组检索。

如果 MorningShift 和 EveningShift 是按上述代码中的键检索的,则每个 MorningShift 和 EveningShift 将有单独的详细信息。

希望您能理解这个问题。感谢您的回复。

这是我尝试使用以下答案的代码。

 var groupedItems = dt.Rows.Cast<DataRow>()
.GroupBy(x => x["Name"])
.OrderBy(x => x["Name"])
.Select(x => new
{
    Name = x["Name"],
    MorningShift = x["MorningShift"]
    EveningShift = x["MorningShift"],
    Details = x.Select(y => new {RouteId=y["RouteId"],ReportingHRName=y["ReportingHRName"],ShiftType=y["ShiftType"]})
});.

但这会给我带来错误。无法在下面的代码中识别 Name、MorningShift 等。

    Name = x["Name"],
    MorningShift = x["MorningShift"]     

【问题讨论】:

  • 您能否提供您的数据表的简化示例和所需的 JSON 结果?
  • 我不明白这里的层次结构在哪里......你在Id上分组,我认为这是独一无二的。那么为什么Details 是一个数组呢?由于Id,它将始终只有一个对象。
  • @AleksandrIvanov:非常感谢您指出这一点。我已经编辑了列表。对困惑感到抱歉。 :(
  • 当您说 DataTable 时,您的意思是标准 DataTable
  • 是的@AleksandrIvanov

标签: c# json linq datatable group-by


【解决方案1】:

您不能在 JSON 中包含行的 Id,因为它不允许进行任何分组(它对于每一行都是唯一的,如果您真的需要可以有一个带有 id 的数组)。要获得所需的 JSON,您只需在 Name 上进行分组。我假设只有MorningShiftEveningShift 列将AllowDbNull 设置为true。如果你也有其他一些属性,你总是需要先用IsColumnNameNull()方法检查它。

所以这里有一个代码可以对你的数据进行分组:

var groupedItems = dt.Rows.Cast<YourDataTableType>()
    .GroupBy(x => x.Name)
    .OrderBy(x => x.Key)
    .Select(x => new
    {
        Name = x.Key,
        MorningShift = x.All(y => y.IsMorningShiftNull())
            ? null
            : x.Sum(y => y.IsMorningShiftNull() ? 0 : (int?) y.MorningShift),
        EveningShift = x.All(y => y.IsEveningShiftNull())
            ? null
            : x.Sum(y => y.IsEveningShiftNull() ? 0 : (int?) y.EveningShift),
        Details = x.Select(y => new {y.RouteId, y.ReportingHRName, y.ShiftType})
    });

然后您可以轻松地将其转换为 JSON。我建议为此使用JSON.NET

var json = JsonConvert.SerializeObject(groupedItems, Formatting.Indented);

JSON 将是:

[
  {
    "Name": "abc",
    "MorningShift": 9,
    "EveningShift": null,
    "Details": [
      {
        "RouteId": 11,
        "ReportingHRName": "alex",
        "ShiftType": "full time"
      }
    ]
  },
  {
    "Name": "def",
    "MorningShift": 8,
    "EveningShift": 6,
    "Details": [
      {
        "RouteId": 12,
        "ReportingHRName": "robin",
        "ShiftType": "part time"
      },
      {
        "RouteId": 13,
        "ReportingHRName": "shek",
        "ShiftType": "part time"
      }
    ]
  },
  {
    "Name": "geh",
    "MorningShift": null,
    "EveningShift": 5,
    "Details": [
      {
        "RouteId": 13,
        "ReportingHRName": "shek",
        "ShiftType": "full time"
      }
    ]
  }
]

编辑:如果您的 DataTable 未键入并且您想按索引访问字段,您的代码可能如下所示:

var groupedItems = dt.Rows.Cast<DataRow>()
    .GroupBy(x => x.Field<string>("Name"))
    .OrderBy(x => x.Key)
    .Select(x => new
    {
        Name = x.Key,
        MorningShift = x.All(y => y.IsNull("MorningShift"))
            ? null
            : x.Sum(y => y.Field<int?>("MorningShift")),
        EveningShift = x.All(y => y.IsNull("EveningShift"))
            ? null
            : x.Sum(y => y.Field<int?>("EveningShift")),
        Details = x.Select(y => new
        {
            RouteId = y.Field<int>("RouteId"),
            ReportingHRName = y.Field<string>("ReportingHRName"),
            ShiftType = y.Field<string>("ShiftType")
        })
    });

当然,如果某些字段有其他数据类型(例如十进制而不是 int),则应该更改它。

PS:在我的原始示例中,我还在 LINQ 中做了一个小修复。这是不正确的。

【讨论】:

  • 我想我明白了。我会试试这个,然后回复你。再次感谢:)
  • 这是您的第一个问题,以防万一...如果答案解决了您的问题,您可以将其标记为已接受。
  • 我无法用您的代码解决它。我无法做到 .GroupBy(x => x.Name)。 x["Name"] 也没有被识别。不确定我是否在某个地方出错了。
  • 我无法选择 MorningShift、EveningShift 作为子列表。
  • 我在 x.Sum(y => y.Field("MorningShift")) 中收到以下错误 System.Data.DataSetExtensions 中发生“System.InvalidCastException”类型的异常.dll 但未在用户代码中处理附加信息:指定的转换无效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-15
  • 1970-01-01
相关资源
最近更新 更多