为何抛出SqlException:IDENTITY_INSERT设置为OFF?CSV入库最优方案
问题分析与解决方案
错误原因
你代码的核心问题是循环外仅创建了一个HeuteDb实例:第一次循环添加并保存后,该实例已被EF上下文跟踪,且数据库为其自动生成了主键ID值。后续循环修改该实例属性后再次调用Add,EF会尝试插入这个已带有ID值的实体,但由于你的ID是自增主键(IDENTITY),默认不允许手动插入ID值,因此抛出IDENTITY_INSERT is set to OFF异常。
修复方案
1. 修正基础代码(解决报错)
将HeuteDb实例的创建移至循环内部,确保每次添加的都是全新的、未被上下文跟踪的实体:
string filePath = @"C:\Users\Elias\Desktop\ASPlearning\DATA\Data.csv"; using (StreamReader reader = new StreamReader(filePath)) { string line; // 若CSV包含表头,先跳过第一行 reader.ReadLine(); while ((line = reader.ReadLine()) != null) { List<string> items = line.Split(';').ToList(); // 每次循环新建实体实例 HeuteDb heute = new HeuteDb(); heute.Uhrzeit = items[0]; heute.Energie = items[1]; _context.HeutesDb.Add(heute); } // 批量提交,减少数据库交互次数 _context.SaveChanges(); }
2. 性能优化:批量操作
原代码每次循环调用SaveChanges()会频繁触发数据库请求,效率极低。改为循环内仅将实体添加到上下文,最后一次性提交,大幅提升性能。
3. 专业CSV读取(规避格式坑)
手动用Split(';')处理CSV容易出现问题:比如字段值包含分号、被引号包裹、存在空值等。推荐使用专门的CSV处理库(如CsvHelper)读取,示例如下:
首先安装NuGet包CsvHelper,然后编写代码:
using CsvHelper; using System.Globalization; string filePath = @"C:\Users\Elias\Desktop\ASPlearning\DATA\Data.csv"; using (var reader = new StreamReader(filePath)) using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) { csv.Configuration.Delimiter = ";"; // 自动映射CSV行到HeuteDb对象 var records = csv.GetRecords<HeuteDb>().ToList(); _context.HeutesDb.AddRange(records); _context.SaveChanges(); }
4. 关于IDENTITY_INSERT的补充
仅当CSV本身包含ID值、需要手动指定主键时,才需要开启IDENTITY_INSERT,示例代码:
using (var transaction = _context.Database.BeginTransaction()) { try { _context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT HeutesDb ON"); _context.HeutesDb.AddRange(records); _context.SaveChanges(); _context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT HeutesDb OFF"); transaction.Commit(); } catch { transaction.Rollback(); throw; } }
你的场景无需此操作,让数据库自动生成自增ID即可。
内容的提问来源于stack exchange,提问作者Elias
相关产品推荐
相关产品推荐

