C# 在比较两个集合时,如何从LINQ更改为使用简单的foreach?

C# 在比较两个集合时,如何从LINQ更改为使用简单的foreach?,c#,C#,我有以下代码,我觉得非常混乱。代码所做的是比较两个集合中的数据,然后根据oldObj和newObj之间的更改发布更新、添加或删除 有没有一种方法可以简化它,使它只包含在三个foreach循环中,这样我就不必使用奇特的LINQ代码。如果有人能给我一个建议,我可以做的第一个变化,只是增加,然后即使是将是一个很大的帮助 var oldObj = db.SubTopics .Where(t => t.TopicId == id) .AsNoTracking() .ToList(); v

我有以下代码,我觉得非常混乱。代码所做的是比较两个集合中的数据,然后根据oldObj和newObj之间的更改发布更新、添加或删除

有没有一种方法可以简化它,使它只包含在三个foreach循环中,这样我就不必使用奇特的LINQ代码。如果有人能给我一个建议,我可以做的第一个变化,只是增加,然后即使是将是一个很大的帮助

var oldObj = db.SubTopics
  .Where(t => t.TopicId == id)
  .AsNoTracking()
  .ToList();
var newObj = topic.SubTopics.ToList();

var upd = newObj
  .Where(wb => oldObj
    .Any(db1 => (db1.SubTopicId == wb.SubTopicId) &&
      (db1.Number != wb.Number || !db1.Name.Equals(wb.Name)
      || !db1.Notes.Equals(wb.Notes))))
    .ToList();

var add = newObj
  .Where(wb => oldObj
    .All(db1 => db1.SubTopicId != wb.SubTopicId))
  .ToList();

var del = oldObj
  .Where(db1 => newObj
    .All(wb => wb.SubTopicId != db1.SubTopicId))
  .ToList();

foreach (var subTopic in upd)
{
  db.SubTopics.Attach(subTopic);
  db.Entry(subTopic).State = EntityState.Modified;
}
foreach (var subTopic in add)
{
  db.SubTopics.Add(subTopic);
}
foreach (var subTopic in del)
{
  db.SubTopics.Attach(subTopic);
  db.SubTopics.Remove(subTopic);
}

如注释中所述,
Where
foreach
的转换非常简单

前两个linq查询(
oldObj
newObj
变量)只是从数据库中选择数据,它们必须保持不变`最后的ToList()表示我们正在对数据库执行查询并实际检索数据

第一句话:

var upd = newObj
  .Where(wb => oldObj
    .Any(db1 => (db1.SubTopicId == wb.SubTopicId) &&
      (db1.Number != wb.Number || !db1.Name.Equals(wb.Name)
      || !db1.Notes.Equals(wb.Notes))))
    .ToList();
表示“从
newObj
集合中获取同样位于
oldObj
中且其至少一个属性已更改的对象”。使用foreach,它将是:

var upd = new List<SubTopic>();
foreach(var newObjElement in newObj) {
    bool wasUpdated = false;
    foreach(var oldObjEl in oldObj) {
        if (oldObjEl.SubTopicId == wb.SubTopicId 
           &&  (db1.Number != wb.Number || !db1.Name.Equals(wb.Name)
           || !db1.Notes.Equals(wb.Notes))) {
            wasUpdated = true;
            break;
        }
        if (wasUpdated) upd.Add(newObjElement);
    }
}
变成:

var add = new List<SubTopic>();
foreach(var newObjElement in newObj) {
    bool wasAdded = true;
    foreach(var oldObjEl in oldObj) {
        if (oldObjEl.SubTopicId != wb.SubTopicId) {
            // do nothing
        }
        else {
            wasAdded = false;
            break;
        }

        if (wasAdded) add.Add(newObjElement);
    }
}
var add=new List();
foreach(newObj中的var newobjeelement){
bool wasAdded=true;
foreach(oldObj中的var oldObjEl){
if(oldObjEl.SubTopicId!=wb.SubTopicId){
//无所事事
}
否则{
wasAdded=false;
打破
}
如果(wasAdded)add.add(newObjElement);
}
}
del
类似于
add


虽然这种转换是可能的,但我强烈反对这样做。
linq
版本确实更具可读性。这就是linq设计的目的。

Where
转换为
foreach
非常简单,如评论中所述

前两个linq查询(
oldObj
newObj
变量)只是从数据库中选择数据,它们必须保持不变`最后的ToList()表示我们正在对数据库执行查询并实际检索数据

第一句话:

var upd = newObj
  .Where(wb => oldObj
    .Any(db1 => (db1.SubTopicId == wb.SubTopicId) &&
      (db1.Number != wb.Number || !db1.Name.Equals(wb.Name)
      || !db1.Notes.Equals(wb.Notes))))
    .ToList();
表示“从
newObj
集合中获取同样位于
oldObj
中且其至少一个属性已更改的对象”。使用foreach,它将是:

var upd = new List<SubTopic>();
foreach(var newObjElement in newObj) {
    bool wasUpdated = false;
    foreach(var oldObjEl in oldObj) {
        if (oldObjEl.SubTopicId == wb.SubTopicId 
           &&  (db1.Number != wb.Number || !db1.Name.Equals(wb.Name)
           || !db1.Notes.Equals(wb.Notes))) {
            wasUpdated = true;
            break;
        }
        if (wasUpdated) upd.Add(newObjElement);
    }
}
变成:

var add = new List<SubTopic>();
foreach(var newObjElement in newObj) {
    bool wasAdded = true;
    foreach(var oldObjEl in oldObj) {
        if (oldObjEl.SubTopicId != wb.SubTopicId) {
            // do nothing
        }
        else {
            wasAdded = false;
            break;
        }

        if (wasAdded) add.Add(newObjElement);
    }
}
var add=new List();
foreach(newObj中的var newobjeelement){
bool wasAdded=true;
foreach(oldObj中的var oldObjEl){
if(oldObjEl.SubTopicId!=wb.SubTopicId){
//无所事事
}
否则{
wasAdded=false;
打破
}
如果(wasAdded)add.add(newObjElement);
}
}
del
类似于
add


虽然这种转换是可能的,但我强烈反对这样做。
linq
版本确实更具可读性。这就是linq设计的目的。

好的。让我们先通过去除任何积垢来简化您所拥有的。您有一些
.ToList()
调用只使用过一次的集合,因此它们只是额外的代码,除了浪费时间和内存外什么也不做。让我们把它们排除在干扰之外:

var oldObj = db.SubTopics
    .Where(t => t.TopicId == id)
    .AsNoTracking()
    .ToList();
var newObj = topic.SubTopics.ToList();

var upd = newObj
    .Where(wb => oldObj
        .Any(db1 => (db1.SubTopicId == wb.SubTopicId) &&
            (db1.Number != wb.Number || !db1.Name.Equals(wb.Name)
            || !db1.Notes.Equals(wb.Notes))));

var add = newObj
    .Where(wb => oldObj
        .All(db1 => db1.SubTopicId != wb.SubTopicId));

var del = oldObj
    .Where(db1 => newObj
        .All(wb => wb.SubTopicId != db1.SubTopicId));

foreach (var subTopic in upd)
{
    db.SubTopics.Attach(subTopic);
    db.Entry(subTopic).State = EntityState.Modified;
}
foreach (var subTopic in add)
{
    db.SubTopics.Add(subTopic);
}
foreach (var subTopic in del)
{
    db.SubTopics.Attach(subTopic);
    db.SubTopics.Remove(subTopic);
}
现在,让我们将它们移动到
foreach
的主体中:

var oldObj = db.SubTopics
    .Where(t => t.TopicId == id)
    .AsNoTracking()
    .ToList();
var newObj = topic.SubTopics.ToList();

foreach (var subTopic in newObj
    .Where(wb => oldObj
        .Any(db1 => (db1.SubTopicId == wb.SubTopicId) &&
            (db1.Number != wb.Number || !db1.Name.Equals(wb.Name)
            || !db1.Notes.Equals(wb.Notes)))))
{
    db.SubTopics.Attach(subTopic);
    db.Entry(subTopic).State = EntityState.Modified;
}
foreach (var subTopic in newObj
    .Where(wb => oldObj
        .All(db1 => db1.SubTopicId != wb.SubTopicId)))
{
    db.SubTopics.Add(subTopic);
}
foreach (var subTopic in oldObj
    .Where(db1 => newObj
        .All(wb => wb.SubTopicId != db1.SubTopicId)))
{
    db.SubTopics.Attach(subTopic);
    db.SubTopics.Remove(subTopic);
}
现在让我们将
.Where()
作为
foreach
逻辑的一部分:

var oldObj = db.SubTopics
    .Where(t => t.TopicId == id)
    .AsNoTracking()
    .ToList();
var newObj = topic.SubTopics.ToList();

foreach (var subTopic in newObj)
{
    if(oldObj.Any(db1 => (db1.SubTopicId == subTopic.SubTopicId) &&
            (db1.Number != subTopic.Number || !db1.Name.Equals(subTopic.Name)
            || !db1.Notes.Equals(subTopic.Notes))))
    {
        db.SubTopics.Attach(subTopic);
        db.Entry(subTopic).State = EntityState.Modified;
    }
}
foreach (var subTopic in newObj)
{
    if(oldObj.All(db1 => db1.SubTopicId != subTopic.SubTopicId))
    {
        db.SubTopics.Add(subTopic);
    }
}
foreach (var subTopic in oldObj)
{
    if(newObj.All(subTopic.SubTopicId != db1.SubTopicId))
    {
        db.SubTopics.Attach(subTopic);
        db.SubTopics.Remove(subTopic);
    }
}
现在让我们将
.Any()
实现为一个循环:

var oldObj = db.SubTopics
    .Where(t => t.TopicId == id)
    .AsNoTracking()
    .ToList();
var newObj = topic.SubTopics.ToList();

foreach (var subTopic in newObj)
{
    foreach(var db1 in oldObj)
    {
        if((db1.SubTopicId == subTopic.SubTopicId) &&
            (db1.Number != subTopic.Number || !db1.Name.Equals(subTopic.Name)
            || !db1.Notes.Equals(subTopic.Notes)))
        {
            db.SubTopics.Attach(subTopic);
            db.Entry(subTopic).State = EntityState.Modified;
            break;
        }
    }
}
foreach (var subTopic in newObj)
{
    foreach(var db1 in oldObj)
    {
        if(db1 => db1.SubTopicId != subTopic.SubTopicId)
        {
            db.SubTopics.Add(subTopic);
            break;
        }
    }
}
foreach (var subTopic in oldObj)
{
    if(newObj.All(subTopic.SubTopicId != db1.SubTopicId)))
    {
        db.SubTopics.Attach(subTopic);
        db.SubTopics.Remove(subTopic);
    }
}
现在,让我们对
All()
执行相同的操作:

最后,让我们从初始列表构造中去掉Linq。请注意,因为这是linq到实体而不是linq到对象,所以我们将数据库中的工作转移到内存中,所以这一位是一个主要的性能杀手。实际上,最好将其替换为对过程的调用,该过程执行中的正在执行的操作,但现在我们开始:

var oldObj = new List<SubTopic>();
foreach(var t in db.SubTopics.AsNoTracking())
    if(t => t.TopicId == id)
        oldObj.Add(t);
var newObj = new List<SubTopic>(topic.SubTopics);

foreach (var subTopic in newObj)
{
    foreach(var db1 in oldObj)
    {
        if((db1.SubTopicId == subTopic.SubTopicId) &&
            (db1.Number != subTopic.Number || !db1.Name.Equals(subTopic.Name)
            || !db1.Notes.Equals(subTopic.Notes)))
        {
            db.SubTopics.Attach(subTopic);
            db.Entry(subTopic).State = EntityState.Modified;
            break;
        }
    }
}
foreach (var subTopic in newObj)
{
    foreach(var db1 in oldObj)
    {
        if(db1 => db1.SubTopicId != subTopic.SubTopicId)
        {
            db.SubTopics.Add(subTopic);
            break;
        }
    }
}
foreach (var subTopic in oldObj)
{
    bool allMatch = true;
    foreach(var db1 in newObj)
    {
        if(subTopic.SubTopicId != db1.SubTopicId)
        {
            allMatch = false;
            break;
        }
    }
    if(allMatch)
    {
        db.SubTopics.Attach(subTopic);
        db.SubTopics.Remove(subTopic);
    }
}
对于如何将某些东西分解成方法,以及如何使用临时方法,人们有一定程度的兴趣,但我认为上面的内容,如果比原始内容更简单的话,更具可读性


删除
oldObj
newObj
上的
ToList()
并将使用
IEnumerable
的方法更改为使用
IQueryable
。这也可能不是因为同一组被多次击中,所以它不像在许多类似的情况下那样是一个不需要动脑筋的问题,但它确实值得一看。

好的。让我们先通过去除任何积垢来简化您所拥有的。您有一些
.ToList()
调用只使用过一次的集合,因此它们只是额外的代码,除了浪费时间和内存外什么也不做。让我们把它们排除在干扰之外:

var oldObj = db.SubTopics
    .Where(t => t.TopicId == id)
    .AsNoTracking()
    .ToList();
var newObj = topic.SubTopics.ToList();

var upd = newObj
    .Where(wb => oldObj
        .Any(db1 => (db1.SubTopicId == wb.SubTopicId) &&
            (db1.Number != wb.Number || !db1.Name.Equals(wb.Name)
            || !db1.Notes.Equals(wb.Notes))));

var add = newObj
    .Where(wb => oldObj
        .All(db1 => db1.SubTopicId != wb.SubTopicId));

var del = oldObj
    .Where(db1 => newObj
        .All(wb => wb.SubTopicId != db1.SubTopicId));

foreach (var subTopic in upd)
{
    db.SubTopics.Attach(subTopic);
    db.Entry(subTopic).State = EntityState.Modified;
}
foreach (var subTopic in add)
{
    db.SubTopics.Add(subTopic);
}
foreach (var subTopic in del)
{
    db.SubTopics.Attach(subTopic);
    db.SubTopics.Remove(subTopic);
}
现在,让我们将它们移动到
foreach
的主体中:

var oldObj = db.SubTopics
    .Where(t => t.TopicId == id)
    .AsNoTracking()
    .ToList();
var newObj = topic.SubTopics.ToList();

foreach (var subTopic in newObj
    .Where(wb => oldObj
        .Any(db1 => (db1.SubTopicId == wb.SubTopicId) &&
            (db1.Number != wb.Number || !db1.Name.Equals(wb.Name)
            || !db1.Notes.Equals(wb.Notes)))))
{
    db.SubTopics.Attach(subTopic);
    db.Entry(subTopic).State = EntityState.Modified;
}
foreach (var subTopic in newObj
    .Where(wb => oldObj
        .All(db1 => db1.SubTopicId != wb.SubTopicId)))
{
    db.SubTopics.Add(subTopic);
}
foreach (var subTopic in oldObj
    .Where(db1 => newObj
        .All(wb => wb.SubTopicId != db1.SubTopicId)))
{
    db.SubTopics.Attach(subTopic);
    db.SubTopics.Remove(subTopic);
}
现在让我们将
.Where()
作为
foreach
逻辑的一部分:

var oldObj = db.SubTopics
    .Where(t => t.TopicId == id)
    .AsNoTracking()
    .ToList();
var newObj = topic.SubTopics.ToList();

foreach (var subTopic in newObj)
{
    if(oldObj.Any(db1 => (db1.SubTopicId == subTopic.SubTopicId) &&
            (db1.Number != subTopic.Number || !db1.Name.Equals(subTopic.Name)
            || !db1.Notes.Equals(subTopic.Notes))))
    {
        db.SubTopics.Attach(subTopic);
        db.Entry(subTopic).State = EntityState.Modified;
    }
}
foreach (var subTopic in newObj)
{
    if(oldObj.All(db1 => db1.SubTopicId != subTopic.SubTopicId))
    {
        db.SubTopics.Add(subTopic);
    }
}
foreach (var subTopic in oldObj)
{
    if(newObj.All(subTopic.SubTopicId != db1.SubTopicId))
    {
        db.SubTopics.Attach(subTopic);
        db.SubTopics.Remove(subTopic);
    }
}
现在让我们将
.Any()
实现为一个循环:

var oldObj = db.SubTopics
    .Where(t => t.TopicId == id)
    .AsNoTracking()
    .ToList();
var newObj = topic.SubTopics.ToList();

foreach (var subTopic in newObj)
{
    foreach(var db1 in oldObj)
    {
        if((db1.SubTopicId == subTopic.SubTopicId) &&
            (db1.Number != subTopic.Number || !db1.Name.Equals(subTopic.Name)
            || !db1.Notes.Equals(subTopic.Notes)))
        {
            db.SubTopics.Attach(subTopic);
            db.Entry(subTopic).State = EntityState.Modified;
            break;
        }
    }
}
foreach (var subTopic in newObj)
{
    foreach(var db1 in oldObj)
    {
        if(db1 => db1.SubTopicId != subTopic.SubTopicId)
        {
            db.SubTopics.Add(subTopic);
            break;
        }
    }
}
foreach (var subTopic in oldObj)
{
    if(newObj.All(subTopic.SubTopicId != db1.SubTopicId)))
    {
        db.SubTopics.Attach(subTopic);
        db.SubTopics.Remove(subTopic);
    }
}
现在,让我们对
All()
执行相同的操作:

最后,让我们从初始列表构造中去掉Linq。请注意,因为这是linq到实体而不是linq到对象,所以我们将数据库中的工作转移到内存中,所以这一位是一个主要的性能杀手。实际上,最好将其替换为对过程的调用,该过程执行中的正在执行的操作,但现在我们开始:

var oldObj = new List<SubTopic>();
foreach(var t in db.SubTopics.AsNoTracking())
    if(t => t.TopicId == id)
        oldObj.Add(t);
var newObj = new List<SubTopic>(topic.SubTopics);

foreach (var subTopic in newObj)
{
    foreach(var db1 in oldObj)
    {
        if((db1.SubTopicId == subTopic.SubTopicId) &&
            (db1.Number != subTopic.Number || !db1.Name.Equals(subTopic.Name)
            || !db1.Notes.Equals(subTopic.Notes)))
        {
            db.SubTopics.Attach(subTopic);
            db.Entry(subTopic).State = EntityState.Modified;
            break;
        }
    }
}
foreach (var subTopic in newObj)
{
    foreach(var db1 in oldObj)
    {
        if(db1 => db1.SubTopicId != subTopic.SubTopicId)
        {
            db.SubTopics.Add(subTopic);
            break;
        }
    }
}
foreach (var subTopic in oldObj)
{
    bool allMatch = true;
    foreach(var db1 in newObj)
    {
        if(subTopic.SubTopicId != db1.SubTopicId)
        {
            allMatch = false;
            break;
        }
    }
    if(allMatch)
    {
        db.SubTopics.Attach(subTopic);
        db.SubTopics.Remove(subTopic);
    }
}
对于如何将某些东西分解成方法,以及如何使用临时方法,人们有一定程度的兴趣,但我认为上面的内容,如果比原始内容更简单的话,更具可读性

删除
oldObj
newObj
上的
ToList()
并将使用
IEnumerable
的方法更改为使用
iQuery>可能是个好主意<