使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象

3 阅读2分钟

在工作中,经常遇到需要对比两个集合的场景,如:

  1. 页面集合数据修改,需要保存到数据库
  2. 全量同步上游数据到本系统数据库

在这些场景中,需要识别出需要新增、更新、删除的数据,由于每次应用是,需要比较的对象类型不一致,因此写了个相对通用的方法。这个过程中,需要理解的有以下2个核心概念:

  1. 唯一标识比较: 如果两个对象的唯一标识相等,则认为这两个对象在业务上代表同一个东西(次要属性是否相等暂不考虑)。
  2. 实体比较:表示两个对象在业务是不是相等(唯一标识相等、次要属性相等)。

代码示例如下:void Main() { // 对比源集合 var source = GenerateStudent(1, 10000, 1000); // 目标集合 var target = GenerateStudent(5000, 10000, 1000);

// 唯一标识比较
Func<Student, Student, bool> keyCompartor = (s, t) => s.Id == t.Id;
// 实体相等比较
Func<Student, Student, bool> entityCompartor = (s, t) => s.Id == t.Id && s.Name.Equals(t.Name) && s.Age == t.Age;

// 新增前准备
Func<Student, Student> insertAction = (s) =>
{
    return new Student
    {
        Id = s.Id,
        Name = s.Name,
        Age = s.Age,
        Operation = "Insert"
    };
};

// 更新前准备
Func<Student, Student, Student> updateAction = (s, t) =>
{
    t.Name = s.Name;
    t.Age = s.Age;
    t.Operation = "Update";

    return t;
};

// 删除前准备
Func<Student, Student> deleteAction = (t) =>
{
    t.Operation = "Delete";
    return t;
};

// 去掉相等对象
RemoveDuplicate(source, target, entityCompartor, (s1, s2) => s1.Id == s2.Id, keyCompartor);

// 需要新增的集合
var insertingStudents = GetInsertingEntities(source, target, keyCompartor, insertAction);
// 需要更新的集合
var updatingStudents = GetUpdatingEntities(source, target, keyCompartor, entityCompartor, updateAction);
// 需要删除的集合
var deletingStudents = GetDeletingEntities(source, target, keyCompartor, deleteAction);

// 后续业务
// InsertStudents(insertingStudents);
// UpdateStudents(updatingStudents);
// DeleteStudents(deletingStudents);

}

// 集合去重 private void RemoveDuplicate<S, T>(List source, List target, Func<S, T, bool> entityCompartor, Func<S, S, bool> sourceKeyCompartor, Func<S, T, bool> keyComportor) { var sameEntities = source.Where(s => target.Exists(t => entityCompartor(s, t))).ToList(); source.RemoveAll(s => sameEntities.Exists(s2 => sourceKeyCompartor(s, s2))); target.RemoveAll(t => sameEntities.Exists(s => keyComportor(s, t))); }

// 获取需要新增的对象集合 private List GetInsertingEntities<S, T>(List source, List target, Func<S, T, bool> keyComportor, Func<S, T> insertAction) { var result = new List(); foreach (var s in source) { var t = target.FirstOrDefault(x => keyComportor(s, x)); if (t == null) { // 目标集合中不存在,则新增 result.Add(insertAction(s)); } }

return result;

}

// 获取需要更新的对象集合 private List GetUpdatingEntities<S, T>(List source, List target, Func<S, T, bool> keyComportor, Func<S, T, bool> entityCompartor, Func<S, T, T> updateAction) { var result = new List(); foreach (var s in source) { var t = target.FirstOrDefault(x => keyComportor(s, x)); if (t != null && !entityCompartor(s, t)) { // 目标集合中存在,但是次要属性不相等,则更新 result.Add(updateAction(s, t)); } }

return result;

}

// 获取需要删除的对象集合 private List GetDeletingEntities<S, T>(List source, List target, Func<S, T, bool> keyComportor, Func<T, T> deleteAction) { var result = new List(); foreach (var t in target) { var s = source.FirstOrDefault(x => keyComportor(x, t)); if (s == null) { // 源集合中存在,目标集合中需要删除 result.Add(deleteAction(t)); } }

return result;

}

// 随机生成测试集合 private List GenerateStudent(int minId, int maxId, int maxNumber) { var r = new Random(); var students = new List(); for (int i = 0; i < maxNumber; i++) { students.Add(new Student { Id = r.Next(minId, maxId), Name = $"name: {r.Next(1, 10)}", Age = r.Next(6, 10) }); }

return students.GroupBy(s => s.Id).Select(s => s.First()).ToList();

}

public class Student { public int Id { get; set; }

public string Name { get; set; }

public int Age { get; set; }

public string Operation { get; set; }

}