当前位置:
首页 > Python基础教程 >
-
C#教程之使用LINQ、Lambda 表达式 、委托快速比较两
本文需要对C#里的LINQ、Lambda 表达式 、委托有一定了解。
在工作中,经常遇到需要对比两个集合的场景,如:
- 页面集合数据修改,需要保存到数据库
- 全量同步上游数据到本系统数据库
在这些场景中,需要识别出需要新增、更新、删除的数据,由于每次应用是,需要比较的对象类型不一致,因此写了个相对通用的方法。这个过程中,需要理解的有以下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
,但实际使用中,两者的类型可以不一样,只要最终返回目标集合的类型就可以了。
上面是我对集合比较的一点心得,只满足了小数据量的业务情景,并没有在大数据量的情况下做过调优。在这里也算是抛砖引玉,大家要是有更好的办法,还希望不吝赐教。
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比
一款纯 JS 实现的轻量化图片编辑器
关于开发 VS Code 插件遇到的 workbench.scm.
前端设计模式——观察者模式
前端设计模式——中介者模式
创建型-原型模式