• 技术文章 >后端开发 >C#.Net教程

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

    云罗郡主云罗郡主2018-10-15 14:07:16转载2288

    本篇文章给大家带来的内容是关于如何使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

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

    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<S> source, List<T> 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<T> GetInsertingEntities<S, T>(List<S> source, List<T> target, Func<S, T, bool> keyComportor,
        Func<S, T> insertAction)
    {
        var result = new List<T>();
        foreach (var s in source)
        {
            var t = target.FirstOrDefault(x => keyComportor(s, x));
            if (t == null)
            {
                // 目标集合中不存在,则新增
                result.Add(insertAction(s));
            }
        }
    
        return result;
    }
    
    // 获取需要更新的对象集合
    private List<T> GetUpdatingEntities<S, T>(List<S> source, List<T> target, Func<S, T, bool> keyComportor,
        Func<S, T, bool> entityCompartor, Func<S, T, T> updateAction)
    {
        var result = new List<T>();
        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<T> GetDeletingEntities<S, T>(List<S> source, List<T> target,
        Func<S, T, bool> keyComportor, Func<T, T> deleteAction)
    {
        var result = new List<T>();
        foreach (var t in target)
        {
            var s = source.FirstOrDefault(x => keyComportor(x, t));
            if (s == null)
            {
                // 源集合中存在,目标集合中需要删除
                result.Add(deleteAction(t));
            }
        }
    
        return result;
    }
    
    // 随机生成测试集合
    private List<Student> GenerateStudent(int minId, int maxId, int maxNumber)
    {
        var r = new Random();
        var students = new List<Student>();
        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; }
    }

    例子中源集合与目标集合使用了相同的对象Student,但实际使用中,两者的类型可以不一样,只要最终返回目标集合的类型就可以了。

    上面是我对集合比较的一点心得,只满足了小数据量的业务情景,并没有在大数据量的情况下做过调优。在这里也算是抛砖引玉,大家要是有更好的办法,还希望不吝赐教。

    以上就是如何使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象(附代码)的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:cnblogs,如有侵犯,请联系admin@php.cn删除
    专题推荐:LINQ Lambda 集合
    上一篇:c# 如何生成自定义图片?c# 生成自定义图片方法 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • Linq to Sql的执行可能无法复用查询计划• mvc3.0 +linq 操作数据库中表的数据(ps:本人菜鸟刚学)• LINQ-to-SQL那点事~LINQ-to-SQL中的数据缓存与应对
    1/1

    PHP中文网