在使用EF Core执行原生SQL查询时,可能会遇到各种报错和问题,本文将详细探讨这些问题及其解决方案,并提供相关示例代码。
一、常见错误及解决方法
1. SQL注入风险
描述:
直接使用字符串拼接生成SQL语句容易导致SQL注入攻击。
解决方案:
使用参数化查询来防止SQL注入,EF Core提供了FromSqlRaw
和FromSqlInterpolated
方法,它们会自动处理参数化查询。
示例代码:
var name = "tom"; var list = db.Set<Person>().FromSqlRaw($"select * from Person where name=@name", new SqlParameter("name", name)).ToList();
2. 查询结果与实体类属性不匹配
描述:
如果查询结果中的列与实体类的属性不完全匹配,会抛出异常。
解决方案:
确保查询结果的列名与实体类的属性名一致,或者使用匿名类型或DTO(数据传输对象)来接收查询结果。
示例代码:
var result = db.Set<Person>().Select(l => new { l.name, l.birthday }).FromSql($"select name, birthday from Person where name=@name", new SqlParameter("@name", "tom")).ToList();
3. 不支持的结构体类型查询
描述:
EF Core不支持对结构体类型的查询结果集进行映射。
解决方案:
如果需要查询非实体类型的数据(如decimal、int等),可以使用DbConnection
直接执行SQL并获取结果。
示例代码:
using (var command = context.Database.GetDbConnection().CreateCommand()) { command.CommandText = "SELECT SUM(ViewCount) FROM Article"; command.CommandType = System.Data.CommandType.Text; var result = await command.ExecuteScalarAsync(); }
4. 无法查询部分列
描述:
FromSqlRaw
和FromSqlInterpolated
方法要求查询结果包含实体类的所有列。
解决方案:
可以使用ExecuteReader
方法来读取部分列的数据,然后手动映射到实体类或DTO。
示例代码:
var sql = "SELECT id, name FROM Person WHERE id = @id"; var person = await context.Database.GetDbConnection().QuerySingleOrDefaultAsync<Person>(sql, new SqlParameter("@id", 1));
5. 视图查询报错
描述:
在EF Core中查询视图时,如果视图中的列名与实体类的属性名不一致,会导致报错。
解决方案:
确保视图中的列名与实体类的属性名一致,或者使用中间层将视图的数据转换为实体类。
示例代码:
public class ViewNoRole : SysNavigation { // 继承SysNavigation并添加额外的字段 } var roleList = _context.Set<ViewNoRole>().FromSql("select * from view_NoRole").ToList();
6. 存储过程调用报错
描述:
调用存储过程时,如果参数传递不正确或存储过程返回的结果集不符合预期,会导致报错。
解决方案:
确保存储过程的参数名称和类型与调用时的参数匹配,并且返回的结果集与实体类的属性一致。
示例代码:
var list = db.Set<Person>().FromSql("exec testproc @id", new SqlParameter("id", 1)).ToList();
二、高级用法及优化建议
1. 自定义扩展方法
如果需要频繁执行复杂的原生SQL查询,可以自定义扩展方法来简化操作,创建一个扩展方法来执行分页查询。
示例代码:
public static class DbContextExtensions { public static List<TEntity> ExecutePagedQuery<TEntity>(this DbContext context, string sql, int pageNumber, int pageSize, params object[] parameters) { var skip = (pageNumber 1) * pageSize; sql += $" OFFSET {skip} ROWS FETCH NEXT {pageSize} ROWS ONLY"; return context.Set<TEntity>().FromSqlRaw(sql, parameters).ToList(); } }
2. 使用Dapper执行复杂查询
对于非常复杂的查询,可以考虑使用Dapper等轻量级的ORM框架,它提供了更高的灵活性和性能。
示例代码:
var sql = "SELECT * FROM Person WHERE name LIKE @name"; var person = connection.Query<Person>(sql, new { name = "%tom%" }).ToList();
1. 归纳
使用参数化查询:避免SQL注入风险。
确保列名匹配:查询结果的列名必须与实体类的属性名一致。
处理非实体类型:对于非实体类型的数据,使用DbConnection
直接执行SQL。
视图查询注意事项:确保视图中的列名与实体类的属性名一致。
存储过程调用:确保参数传递正确且结果集符合预期。
自定义扩展方法:简化频繁执行的复杂查询。
使用Dapper:对于非常复杂的查询,考虑使用Dapper等框架。
2. FAQs
Q1:如何在EF Core中防止SQL注入?
A1:使用FromSqlRaw
和FromSqlInterpolated
方法,并传递参数化的SQL语句。
var name = "tom"; var list = db.Set<Person>().FromSqlRaw($"select * from Person where name=@name", new SqlParameter("name", name)).ToList();
Q2:EF Core中如何查询视图?
A2:确保视图中的列名与实体类的属性名一致,或者使用中间层将视图的数据转换为实体类。
public class ViewNoRole : SysNavigation { // 继承SysNavigation并添加额外的字段 } var roleList = _context.Set<ViewNoRole>().FromSql("select * from view_NoRole").ToList();