EF6搭配SQLite使用SqlQuery查询时抛出索引超出数组界限异常
EF6 + SQLite 下 SqlQuery 抛出数组越界异常的解决方案
这个异常和你调用ToList()/ToArray()/foreach哪种方式执行查询无关,错误发生在EF内部的结果集到实体的映射阶段,和SQL语句本身的写法没有直接关系,按优先级排查以下问题即可:
- 优先检查
System.Data.SQLite.EF6程序包版本兼容性:这是该场景下最高发的已知bug,当你使用EF 6.2以上版本(尤其是6.4.x系列),搭配1.0.115及以下版本的SQLite EF6提供程序时,无论执行什么查询,映射阶段都会固定抛出「索引超出数组界限」异常。直接通过NuGet将System.Data.SQLite、System.Data.SQLite.EF6、System.Data.SQLite.Linq三个关联包升级到最新稳定版,清理解决方案重新编译即可解决。 - 其次检查实体映射配置一致性:
- 确认
GpsCompassDatagram类中,不需要映射到数据库的公共属性(计算属性、导航属性、复杂类型属性)都标记了[NotMapped]特性。SqlQuery做强类型映射时会默认匹配所有公共属性,一旦属性数量、类型和查询返回的列不匹配,老版本SQLite提供程序不会抛出明确的映射错误,只会触发数组越界异常。 - 如果是Database First模式,检查edmx模型文件的映射元数据是否和实际表结构同步,有没有手动删改表字段后没有更新模型的情况。
- 可以临时用
db.Database.SqlQuery<dynamic>(SqlQuery).ToList()执行查询做验证,如果动态类型能正常拿到结果,就能100%确认问题出在实体映射层,和SQL执行逻辑无关。
- 确认
- 关于运行时动态指定返回列的LINQ实现方案,不建议用字符串拼接原生SQL的方式,推荐两种更稳妥的实现:
- 如果可选列范围固定,提前写好对应列组合的LINQ投影分支,运行时根据传入参数选择对应分支即可,完全规避SQL注入风险。
- 如果列组合完全动态,引入
System.Linq.Dynamic.Core包,通过动态表达式实现投影查询,示例代码如下:
internal static List<GpsCompassDatagram> GetAllGpsCompassRecsByColumns(string columnsToRetrieve) { // 务必在这里加列名白名单校验,禁止传入白名单外的字符串,避免SQL注入 var allowedColumns = new HashSet<string> { "Id", "GpsTime", "Lon", "Lat", "CompassAngle" /* 把所有允许查询的列列在这里 */ }; var columnList = columnsToRetrieve.Split(',').Select(c => c.Trim()).Where(c => allowedColumns.Contains(c)); if (!columnList.Any()) throw new ArgumentException("指定的查询列不合法"); using (CdSetEntities db = new CdSetEntities()) { return db.GpsCompassDatagrams.Select($"new({string.Join(",", columnList)})").ToList<GpsCompassDatagram>(); } }
注意:所有动态传入列名的场景必须做白名单校验,禁止直接拼接用户传入的原始字符串,避免SQL注入漏洞。
内容的提问来源于stack exchange,提问作者AeroClassics
相关产品推荐
相关产品推荐

