【问题标题】:null output when flattening nested lists of objects with LINQ select many使用 LINQ 展平对象的嵌套列表时输出 null 多选
【发布时间】:2019-10-28 14:42:19
【问题描述】:

我有一个嵌套了几层的对象列表...我正在尝试将此列表展平为一个新的匿名类型列表...或者我可以创建一个新类,它不会没关系...我只需要能够在树的各个级别选择特定数据并将其展平即可。

嵌套结构如下所示:

TopResult contains -> List<StepResult> each StepResult contains -> List<PropResult> each PropResult contains -> List<PropBinary>

我需要展平嵌套结构,并且由于某种原因,当我尝试使用 SelectMany 进行此操作时,我的结果一直是 null... 这是我正在尝试做的事情:

var resultsModified = results
                .SelectMany(x => (x.StepResults == null ? new List<StepResult>() : x.StepResults)
                .SelectMany(s => (s.PropResults == null ? new List<PropResult>() : s.PropResults)
                .SelectMany(p => (p.PropBinary == null ? new List<PropBinary>() : p.PropBinary)
                .Select(b => new
                {
                    SerialNumber = x.SerialNumber,
                    StationId = x.StationId,
                    StartTime = x.StartTime,
                    ExecutionTime = x.ExecutionTime,
                    Status = x.Status,
                    UserLogin = x.UserLogin,
                    StepName = s == null ? "" : s.StepName,
                    StepType = s == null ? "" : s.StepType,
                    StepOrderNumber = s == null ? null : s.OrderNumber,
                    SequenceName = s == null ? "" : s.SequenceName,
                    StepStatus = s == null ? "" : s.Status,
                    PropName = p == null ? "" : p.Name,
                    PropTypeName = p == null ? "" : p.TypeName,
                    PropTypeValue = p == null ? null : p.TypeValue,
                    PropOrderNumber = p == null ? null : p.OrderNumber,
                    PropCategory = p == null ? null : p.Category,
                    PropData = p == null ? null : p.Data,
                    PropBinaryData = b == null ? null : b.Data
                })))).ToList();

所有的列表都被初始化为新的列表,所以它们都不应该是空的……但我还是在做空检查,因为此时我想不出还有什么可以尝试的。

有没有人可以指出我缺少什么才能使这个嵌套列表变平?

【问题讨论】:

  • 一些扩展方法会让这段代码更漂亮
  • 我目前有一些扩展可以清除所有空检查...只是没有拉入我编写它们的库。您还有其他推荐的扩展方法吗?

标签: c# linq nested


【解决方案1】:

我很确定你的括号 jimmy 被顶了。但它仍然可以编译。你想要这样的括号:

resultsModified = results
                .SelectMany(x => (x.StepResults == null ? new List<StepResult>() : x.StepResults))  //extra one here
                .SelectMany(s => (s.PropResults == null ? new List<PropResult>() : s.PropResults)) //extra one here
                .SelectMany(p => (p.PropBinary == null ? new List<PropBinary>() : p.PropBinary)) //extra one here
                .Select(b => new
                {
                    SerialNumber = x.SerialNumber,
                    StationId = x.StationId,
                    StartTime = x.StartTime,
                    ExecutionTime = x.ExecutionTime,
                    Status = x.Status,
                    UserLogin = x.UserLogin,
                    StepName = s == null ? "" : s.StepName,
                    StepType = s == null ? "" : s.StepType,
                    StepOrderNumber = s == null ? null : s.OrderNumber,
                    SequenceName = s == null ? "" : s.SequenceName,
                    StepStatus = s == null ? "" : s.Status,
                    PropName = p == null ? "" : p.Name,
                    PropTypeName = p == null ? "" : p.TypeName,
                    PropTypeValue = p == null ? null : p.TypeValue,
                    PropOrderNumber = p == null ? null : p.OrderNumber,
                    PropCategory = p == null ? null : p.Category,
                    PropData = p == null ? null : p.Data,
                    PropBinaryData = b == null ? null : b.Data
                }).ToList(); //remove most of these

我认为这种方法可能更容易阅读:

    var results = list.SelectMany(x => x.StepResults)
                      .Where(x => x != null)
                      .SelectMany(x => x.PropResults)
                      .Where(x => x != null)
                      // etc etc;

【讨论】:

  • 根据允许的 C# 版本,空传播 ? 和空合并 ?? 运算符可以稍微清理一下。例如:PropBinaryData = b?.DataStepName = s?.StepName ?? ""
  • 所以我想我尝试了这种方法,除了链中的最后一个变量之外,我无法访问任何变量,这就是为什么您会看到我在上面尝试的方式。我的假设是我只能在他们自己的选择中访问 x、s、p 和 b……当我在实践中尝试时,我似乎无法访问它们。你能编译这段代码吗?我想我会再试一次并报告。
  • 是的,我刚刚测试了验证,但您发布的这段代码不起作用...变量 x、s 和 p 在对 new 的调用中不可访问
【解决方案2】:

我终于找到了一个符合我预期的解决方案。这并不理想,老实说有点丑……但这是我能找到的唯一方法。

总而言之,我需要将每个 SelectMany 分解为自己的语句,在该语句中我根据需要按各种对象对数据进行分组,然后通过一系列连接来按预期获取数据。

代码:

var uutData = results.Select(x => new UutData
            {
                UutResultId = x.Id,
                ...
            }).ToList();

var stepData = results.SelectMany(x => (x.StepResults == null ? new List<StepResult>() : x.StepResults), (x, s) => new StepData
                {
                    UutResultId = x.Id,
                    StepResultId = s.Id,
                    ...
                }).ToList();

var propData = stepData.SelectMany(s => (s.PropResults == null ? new List<PropResult>() : s.PropResults), (s, p) => new PropData
            {
                StepResultId = s.StepResultId,
                PropResultId = p.Id,
                ...
            }).ToList();

//... continue getting nested data objects as needed

var joined = from propStep in (from uutStep in (from uut in uutData
                                             join step in stepData on uut.UutId equals step.UutResultId into step2
                                             from subStep in step2.DefaultIfEmpty()
                                             select new
                                             {
                                                 uut.UutSerialNumber,
                                                 ...
                                                 StepName = subStep == null ? "" : subStep.StepName,
                                                 ...
                                                 StepResultId = subStep == null ? "" : subStep.StepResultId
                                             })
                            join prop in propData on uutStep.StepResultId equals prop.StepResultId into prop2
                            from subProp in prop2.DefaultIfEmpty()
                            select new
                            {
                                uutStep.UutSerialNumber,
                                ...
                                uutStep.StepResultId,
                                PropName = subProp == null ? "" : subProp.PropName,
                                ...
                                PropResultId = subProp == null ? "" : subProp.PropResultId
                            })
                            //... continue joins as needed for all nested objects generated above
                            //appending data from each previous join to carry it all the way through to the final object
                            //to get objects back as List<object>, call .ToList() on entire query (use parentheses to encapsulate entire query)

请注意,我在尝试缩短此答案的代码的位置插入了省略号。省略号只需替换为添加到对象的所有字段即可。

在所有连接之后,最终返回的 List 返回一个顶级对象,其中包含我需要的嵌套对象中的所有相关数据。所以数据现在不再嵌套。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-19
    • 1970-01-01
    • 2017-01-21
    • 1970-01-01
    • 2021-05-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多