使用LINQ.有两个不同的列表.如何识别不匹配的对象

Sam*_*tar 7 c# linq

我有三个班:

public partial class Objective{
    public Objective() {
        this.ObjectiveDetails = new List<ObjectiveDetail>();
    }
    public int ObjectiveId { get; set; }
    public int Number { get; set; }
    public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
}
public partial class ObjectiveDetail {
    public ObjectiveDetail() {
        this.SubTopics = new List<SubTopic>();
    }
    public int ObjectiveDetailId { get; set; }
    public int Number { get; set; }
    public string Text { get; set; }
    public virtual ICollection<SubTopic> SubTopics { get; set; }
}
public partial class SubTopic {
    public int SubTopicId { get; set; }
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我有两个清单:

IList<ObjectiveDetail> oldObj;
IList<ObjectiveDetail> newObj;
Run Code Online (Sandbox Code Playgroud)

以下LINQ为我提供了一个新的ObjectiveDetail对象列表,其中:列表中任何ObjectiveDetail对象的Number或Text字段在oldObj和之间不同newObj.

IList<ObjectiveDetail> upd = newObj
    .Where(wb => oldObj
        .Any(db => (db.ObjectiveDetailId == wb.ObjectiveDetailId) && 
                   (db.Number != wb.Number || !db.Text.Equals(wb.Text))))
     .ToList();
Run Code Online (Sandbox Code Playgroud)

我怎么能修改此所以LINQ给了我一个新的列表ObjectiveDetail对象,其中:数字或文本字段或副标题收集任何ObjectiveDetail列表中的对象之间的差异oldObjnewObj.

换句话说,如果符合以下条件ObjectiveDetail,我希望将其添加到upd列表中:

  • 它在oldObj中的Text与newObj中的Text不同
  • 它在oldObj中有一个与newObj中的Number不同的Number
  • 它有一个SubTopics集合,其中包含oldObj中的三个元素和newObj中的4个元素
  • 它有一个SubTopics集合,oldObj中没有元素,newObj中没有2个元素
  • 它有一个SubTopics集合,在oldObj中有2个元素,在newObj中没有元素
  • 它有一个SubTopics集合,其中元素在oldObj中SubTopicId为1和2,在newObj中为1和3

我希望有人可以在我已经拥有的LINQ语句中提出一些额外的内容.

Kon*_*osa 7

我将在两个列表(交集)中创建一个相同对象的列表,而不是创建一个巨大且难以管理的可验证LINQ查询,因此,除了此交集之外,还要获取两个集合的总和.要比较对象,您可以使用IEqualityComparer<>实现.这是一个草案:

public class ObjectiveDetailEqualityComparer : IEqualityComparer<ObjectiveDetail>
{
    public bool Equals(ObjectiveDetail x, ObjectiveDetail y)
    {
        // implemenation                          
    }

    public int GetHashCode(ObjectiveDetail obj)
    {
        // implementation
    }
}
Run Code Online (Sandbox Code Playgroud)

然后简单地说:

var comparer = new ObjectiveDetailEqualityComparer();
var common = oldObj.Intersect(newObj, comparer);
var differs = oldObj.Concat(newObj).Except(common, comparer);
Run Code Online (Sandbox Code Playgroud)

当类改变时(新属性等),这将更容易维护.


Obl*_*age 3

这应该是您所需要的:

IList<ObjectiveDetail> upd = newObj.Where(wb =>
            oldObj.Any(db =>
                (db.ObjectiveDetailId == wb.ObjectiveDetailId) &&
                    (db.Number != wb.Number || !db.Text.Equals(wb.Text)
                    || db.SubTopics.Count != wb.SubTopics.Count
                    || !db.SubTopics.All(ds => wb.SubTopics.Any(ws =>
                                     ws.SubTopicId == ds.SubTopicId))
                    ))).ToList();
Run Code Online (Sandbox Code Playgroud)

怎么运行的

db.SubTopics.Count != wb.SubTopics.Count确认正在比较的新对象 ( wb) 和正在比较的旧对象 ( db) 具有相同数量的 SubTopics。这部分非常简单。

!db.SubTopics.All(ds => wb.SubTopics.Any(ws => ws.SubTopicId == ds.SubTopicId))有点复杂。All()如果给定表达式对于集合的所有成员都为 true,则该方法返回 true。Any()如果给定表达式对于集合的任何成员都为 true,则该方法返回 true。因此,整个表达式会检查ds旧对象中的每个子主题在新对象中db是否存在ws具有相同 ID 的子主题wb

基本上,第二行确保旧对象中存在的每个子主题也存在于新对象中。第一行确保新旧对象具有相同数量的 SubTopics;否则,第二行将认为具有子主题 1 和 2 的旧对象与具有子主题 1、2 和 3 的新对象相同。


注意事项

此添加不会检查子主题是否相同Name;如果您还需要检查,请将ws.SubTopicId == ds.SubTopicId第二行中的 更改为ws.SubTopicId == ds.SubTopicId && ws.Name.Equals(ds.Name)

如果 ObjectiveDetail 可以包含多个具有相同 SubTopicId 的 SubTopic(即,如果 SubTopicId 不唯一),则此添加将无法正常工作。如果是这种情况,您需要将第二行替换为!db.SubTopics.All(ds => db.SubTopics.Count(ds2 => ds2.SubTopicId == ds.SubTopicId) == wb.SubTopics.Count(ws => ws.SubTopicId == ds.SubTopicId))。这将检查每个 SubTopicId 在新对象中出现的次数是否与在旧对象中出现的次数完全相同。

此添加不会检查新对象和旧对象中的子主题是否处于相同的顺序。为此,您需要将第二行替换为db.SubTopics.Where((ds, i) => ds.SubTopicId == wb.SubTopics[i].SubTopicId).Count != db.SubTopics.Count. 请注意,此版本还处理非唯一的 SubTopicId 值。确认旧对象中的SubTopic数量使得新对象中相同位置的SubTopic相同等于旧对象中的SubTopic总数(即对于旧对象中的每个SubTopic,新对象中相同位置的SubTopic是相同的)。


高层次的想法

从可维护性的角度来看,Konrad Kokosa 的答案更好(我已经投了赞成票)。如果您不希望经常重新访问该语句,我只会使用像这样的又大又难看的 LINQ 语句。如果您认为确定两个ObjectiveDetail对象是否相等的方式可能会发生变化,或者使用此语句的方法可能需要重新设计,或者该方法足够重要,以至于第一次接触该代码的人需要重新编写该方法。能够快速理解它,那么就不要使用大而长的 LINQ。