【问题标题】:Combine two list using Linq and add data as needed from different tables使用 Linq 合并两个列表,并根据需要从不同的表中添加数据
【发布时间】:2021-10-26 12:42:30
【问题描述】:

我需要更改一个流程,并且已经为此苦苦挣扎了几天。 当前任务检查用户在表 1 中输入的所有数字。我没有这个问题,因为我可以用这个语句返回它:

var itemsTable1 = db.Table1.Where(a =>
    searchNumbers.Contains(a.Digit1) || searchNumbers.Contains(a.Digit2) || searchNumbers.Contains(a.Digit3) ||
    searchNumbers.Contains(a.Digit4) || searchNumbers.Contains(a.Digit5) || _Digit6 == a.Digit6 && a.ValidFlag == 1
).ToList();

现在我需要在 Table2 上查找相同的数字,并确保我也带上了这些数字。尽管这些表将具有相同的数字列,但它们的总列数不会相同。我可以对 Table2 进行上述的另一个陈述,那里没有问题。但是,我还需要携带不包含数字但具有相同 ID 的记录。所以,我的场景是这样的:

Table1 = 包含数字 -> Table2 != 包含数字
Table2 = 包含数字 -> Table1 != 包含数字
Table1 = 包含数字 -> Table2 = 包含数字

最后,我需要按降序显示任一列表上的数据,我假设我必须合并两个/三个列表并将其返回给模型。

有没有办法用普通的 Linq 做到这一点?还是我最好在存储过程中创建一个 CTE 并在那里传递参数然后在 EF 中调用?

【问题讨论】:

    标签: c# asp.net-mvc linq


    【解决方案1】:

    我假设你需要这个查询:

    var query = 
        from t1 in db.Table1
        join t2 in db.Table2 on t1.Id equals t1.Id
        let t1Contains = searchNumbers.Contains(t1.Digit1) 
                    || searchNumbers.Contains(t1.Digit2) 
                    || searchNumbers.Contains(t1.Digit3) 
                    || searchNumbers.Contains(t1.Digit4) 
                    || searchNumbers.Contains(t1.Digit5) 
                    || _Digit6 == t1.Digit6 && t1.ValidFlag == 1
        let t2Contains = searchNumbers.Contains(t2.Digit1) 
                    || searchNumbers.Contains(t2.Digit2) 
                    || searchNumbers.Contains(t2.Digit3) 
                    || searchNumbers.Contains(t2.Digit4) 
                    || searchNumbers.Contains(t2.Digit5) 
                    || _Digit6 == t2.Digit6 && t2.ValidFlag == 1
        where t1Contains != t2Contains || t1Contains && t2Contains
        select 
        {
            t1,
            t2
        };
    

    请注意,您没有指定所需的输出以及如何订购结果。

    【讨论】:

    • 我认为你在这里是正确的。但是,这现在引入了一个新错误:“无法将 lambda 表达式分配给范围变量”,我认为这是因为为 searchNumbers 传递了数组。关于如何解决这个问题的任何想法?
    • 糟糕,抱歉删除了 lambda。
    • 没问题。谢谢你。查询现在返回两个表上的内容,而不是其中一个表中的内容。 Table1 有数千条记录,而 Table2 则只有几条记录。
    • 那么就需要Concat了。最好指定两个表中需要哪些字段。
    【解决方案2】:

    遵循@Svyatoslav Danyliv 的建议。我创建了以下内容:

     //By using the list, we make sure that the search returns every single digit, regardless of position they occupy in the DB
                            var itemsT1 = db.Table1.Where(a => searchNumbers.Contains(a.Digit1) || searchNumbers.Contains(a.Digit2) || searchNumbers.Contains(a.Digit3) ||
                                               searchNumbers.Contains(a.Digit4) || searchNumbers.Contains(a.Digit5) || _Digit6 == a.Digit6 && a.ValidDrawResults == 1);
                                   
    
                            var itemsT2 = db.Table2.Where(a => searchNumbers.Contains(a.Digit1) || searchNumbers.Contains(a.Digit2) || searchNumbers.Contains(a.Digit3) ||
                                               searchNumbers.Contains(a.Digit4) || searchNumbers.Contains(a.Digit5) || _Digit6 == a.Digit6 && a.ValidDrawResults == 1);                                   
    
                            //Create list to hold Ids from the records above
                            List<int?> t1Ids = new List<int?>();
                            List<int?> t2Ids = new List<int?>();
    
                            //Insert the Ids into the lists
                            foreach (var t1Id in t1Ids )
                            {
                                t1Ids.Add((int)t1Id.Id);
                            }
    
                            foreach (var t2Id in t2Ids)
                            {
                                t2Ids.Add((int)t2Id.Id);
                            }
    
                            //Get the records from opposite table that contains same Ids
                            var resultT1 = db.Table1.Where(r => t1Ids.Contains(r.Id)
                                                            );
    
                            var resultT2 = db.Table2.Where(r => t2Ids.Contains(r.Id)
                                                            );
    
                            //Combine the lists to pass to the view
                            var groupedT1 = itemsT1.Concat(resultT1).Distinct();
                            var groupedT2 = itemsT2.Concat(resultT2).Distinct();
    
                            using (db)
                            {
                                var vmT1T2 = new ViewModelTables
                                {
                                    getTable1 = groupedT2.ToList(),
                                    getTable2 = groupedT2.ToList()
                                };
                                return View(vmT1T2);
                            }
    

    就带来我需要的记录而言,它的效果非常好。 再次感谢@Svyatoslav Danyliv 为我指明了正确的方向。我很感激并希望这对其他人也有帮助。

    【讨论】:

    • 嗯,它可以更好;) EF 不支持 FULL OUTER JOIN,这在您的情况下是必需的,但可以模拟。
    • 当然。但是,这确实最终完成了工作。只要它不会在工作流程中增加太多,从性能上讲,我不介意稍微绕开马车。再次感谢。
    • 您可以通过两次 LEFT JOIN 来做到这一点,并且不收集密钥。当 ID 过多时,包含将失败。
    • 当您的解决方案有效时,您可能希望避免不必要地调用.ToList()。每次执行此操作时,都会枚举整个列表并将其内容复制到新的List&lt;T&gt;。这可能很昂贵,并且每个List&lt;T&gt; 都存储在堆上,增加了内存压力,导致不必要的垃圾回收。仅在绝对需要时转换为List&lt;T&gt;。在您的解决方案中,我至少数了其中的 10 个;如果您不打算在序列中添加或删除项目,IEnumerable&lt;T&gt; 就足够了。
    • @MikeHofer,感谢您的提示,确实有道理。我很感激并修复了上面的代码以反映您的建议。
    猜你喜欢
    • 2018-04-03
    • 1970-01-01
    • 2013-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-05
    • 2012-02-24
    相关资源
    最近更新 更多