C#中如何剔除List<T>与DbSet<T>双属性匹配的重复数据?
按Description和Date去重后将CSV数据存入数据库
核心思路
先从数据库提取已存在的(Description, Date)组合,用高效的集合类型快速匹配过滤CSV列表,最后批量插入新数据。
实现步骤及代码示例
假设你已经通过依赖注入获取了DbContext实例(比如在Program.cs中通过builder.Services.AddDbContext<YourDbContext>()配置后,用var dbContext = app.Services.GetRequiredService<YourDbContext>()获取):
- 查询数据库中已存在的匹配组合
只提取需要的两个字段,避免加载整个实体,提升性能:
// 注意:如果Date字段包含时间部分,建议只取Date部分避免精度问题 var existingTransactionPairs = await dbContext.Transactions .Select(t => new { t.Description, t.Date.Date }) .ToListAsync();
- 转换为HashSet实现快速查找
HashSet的Contains操作时间复杂度为O(1),远高于循环比对:
var existingPairSet = new HashSet<(string Description, DateTime Date)>( existingTransactionPairs.Select(p => (p.Description, p.Date)) );
- 过滤CSV列表中的重复数据
保留数据库中不存在的记录:
var transactionsToAdd = transactionsCSV .Where(t => !existingPairSet.Contains((t.Description, t.Date.Date))) .ToList();
- 批量插入并保存到数据库
if (transactionsToAdd.Any()) { await dbContext.Transactions.AddRangeAsync(transactionsToAdd); await dbContext.SaveChangesAsync(); }
注意事项
- 时间精度问题:如果CSV中的
Date带具体时间,而数据库中只存日期,务必统一取Date部分(如t.Date.Date),避免因时分秒差异导致误判。 - 大小写敏感:如果需要忽略Description的大小写,可在查询和过滤时统一转成小写/大写:
// 查询时转小写 var existingTransactionPairs = await dbContext.Transactions .Select(t => new { Description = t.Description.ToLower(), t.Date.Date }) .ToListAsync(); // HashSet和过滤时同步转小写 var existingPairSet = new HashSet<(string Description, DateTime Date)>( existingTransactionPairs.Select(p => (p.Description, p.Date)) ); var transactionsToAdd = transactionsCSV .Where(t => !existingPairSet.Contains((t.Description.ToLower(), t.Date.Date))) .ToList(); - 性能优化:如果数据量极大,可考虑分批次查询和插入,避免内存占用过高。
内容的提问来源于stack exchange,提问作者Musaffar Patel
相关产品推荐
相关产品推荐

