使用 Linq 合并两个列表,并根据需要从不同的表中添加数据
Posted
技术标签:
【中文标题】使用 Linq 合并两个列表,并根据需要从不同的表中添加数据【英文标题】:Combine two list using Linq and add data as needed from different tables 【发布时间】: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 中调用?
【问题讨论】:
【参考方案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<T>
。这可能很昂贵,并且每个List<T>
都存储在堆上,增加了内存压力,导致不必要的垃圾回收。仅在绝对需要时转换为List<T>
。在您的解决方案中,我至少数了其中的 10 个;如果您不打算在序列中添加或删除项目,IEnumerable<T>
就足够了。
@MikeHofer,感谢您的提示,确实有道理。我很感激并修复了上面的代码以反映您的建议。以上是关于使用 Linq 合并两个列表,并根据需要从不同的表中添加数据的主要内容,如果未能解决你的问题,请参考以下文章
查询 - 数据集中的全外连接2个不同的表 - LINQ C#