我有三个班:
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列表中的对象之间的差异oldObj和newObj.
换句话说,如果符合以下条件ObjectiveDetail,我希望将其添加到upd列表中:
我希望有人可以在我已经拥有的LINQ语句中提出一些额外的内容.
我将在两个列表(交集)中创建一个相同对象的列表,而不是创建一个巨大且难以管理的可验证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)
当类改变时(新属性等),这将更容易维护.
这应该是您所需要的:
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。