文章目录
- 前言
- 一、执行查询(返回数据)
- 1) 使用 FromSqlRaw或 FromSqlInterpolated 方法,适用于 DbSet<T>,返回实体集合。
- 2)结合 LINQ 查询
- 3)执行任意原生SQL查询语句(使用ADO.Net)
- 二、执行非查询操作(增删改)
- 1)使用 ExecuteSqlRaw() 或 ExecuteSqlInterpolated() 、ExecuteSqlInterpolatedAsync()方法,返回受影响的行数。
- 三、调用存储过程
- 1)执行查询存储过程
- 2)执行非查询存储过程
- 四、事务处理
- 1)确保多个 SQL 操作原子性
- 五、原生 SQL 查询的注意事项
- 总结
前言
为什么要写原生SQL语句?
- 尽管EF Core已经非常强大,但是任然存在着无法被写成标准EF Core调用方法的SQL语句,少数情况下任然需要写原生SQL。
- 存在问题:可能无法跨数据库操作。
一、执行查询(返回数据)
1) 使用 FromSqlRaw或 FromSqlInterpolated 方法,适用于 DbSet,返回实体集合。
示例:查询数据(建议使用FromSqlInterpolated,防止SQL注入)
var blogs = context.Blogs.FromSqlRaw("SELECT * FROM Blogs WHERE Rating > {0}", 3).ToList();// 或使用插值字符串(参数化,防注入)
var rating = 3;
var blogsSafe = context.Blogs.FromSqlInterpolated($"SELECT * FROM Blogs WHERE Rating > {rating}").ToList();
2)结合 LINQ 查询
var filteredBlogs = context.Blogs.FromSqlInterpolated("SELECT * FROM Blogs").Where(b => b.Url.Contains("dotnet")).OrderBy(b => b.Rating).ToList();
3)执行任意原生SQL查询语句(使用ADO.Net)
DbConnection conn=dbContext.Database.GetDbConnection();
if (conn.State != ConnectionState.Open)
{ conn.Open();
}
using (var cmd = conn.CreateCommand())
{cmd.CommandText = "select Title,Content from T_Articles where Title like '%反反复复%'";using (var reader = await cmd.ExecuteReaderAsync()){while (await reader.ReadAsync()){string title = reader.GetString(0);string content= reader.GetString(1);Console.WriteLine(title+","+content);}}}
二、执行非查询操作(增删改)
1)使用 ExecuteSqlRaw() 或 ExecuteSqlInterpolated() 、ExecuteSqlInterpolatedAsync()方法,返回受影响的行数。
示例:更新数据
var rowsAffected = context.Database.ExecuteSqlRaw("UPDATE Blogs SET Rating = 5 WHERE Name LIKE '%EF Core%'");// 参数化示例
var minRating = 3;
var rowsSafe = await context.Database.ExecuteSqlInterpolatedAsync($"DELETE FROM Blogs WHERE Rating < {minRating}");
三、调用存储过程
1)执行查询存储过程
var blogs = context.Blogs.FromSqlRaw("EXEC GetTopRatedBlogs @p0", 5).ToList();
2)执行非查询存储过程
context.Database.ExecuteSqlRaw("EXEC ArchiveOldBlogs @p0", DateTime.Now.AddYears(-1));
四、事务处理
1)确保多个 SQL 操作原子性
using (var transaction = context.Database.BeginTransaction())
{try{context.Database.ExecuteSqlRaw("UPDATE Blogs SET Rating = 5 WHERE BlogId = 1");context.Database.ExecuteSqlRaw("DELETE FROM Blogs WHERE BlogId = 2");transaction.Commit();}catch{transaction.Rollback();}
}
五、原生 SQL 查询的注意事项
- 参数化查询:始终使用参数化输入(如 {0} 或插值语法)避免 SQL 注入。
- 列匹配:查询返回的列名必须与实体属性名匹配,或通过 AS 别名映射。
- 跟踪变更:默认跟踪实体变更,若无需跟踪可加 .AsNoTracking()。
- 数据库兼容性:SQL 语法需适配具体数据库(如 SQL Server 和 SQLite 的差异)。
- 性能:仅在必要时使用原生 SQL,优先选择 LINQ 以保证类型安全和可维护性。
总结
一般情况下Linq操作就足够了,尽量不要用原生SQL。
- EF Core 的原生 SQL 方法适用于特定场景,但需谨慎使用以确保安全性和可维护性。优先考虑 LINQ,复杂场景再选择原生 SQL。
- 非查询SQL用ExecuteSqlInterpolatedAsync()
- 针对实体的SQL查询用FromSqlInterpolated()
- 复杂SQL查询用ADO.NET的方式或者Dapper等。