目錄
EFCore執行非查詢原生SQL語句
為什么要寫原生SQL語句
執行非查詢SQL語句
有SQL注入漏洞
ExecuteSqlInterpolatedAsync?
其他方法
執行實體相關查詢原生SQL語句
FromSqlInterpolated
局限性
執行任意原生SQL查詢語句
什么時候用ADO.NET
執行任意SQL
Dapper
總結
EFCore執行非查詢原生SQL語句
為什么要寫原生SQL語句
- 盡管EF Core已經非常強大,但是仍然存在著無法被寫成標準EF Core調用方法的SQL語句,少數情況下仍然需要寫原生SQL。
- 可能無法跨數據庫。
- 三種情況:非查詢語句、實體查詢、任意SQL查詢。
執行非查詢SQL語句
使用dbCtx.Database. ExecuteSqlInterpolated ();
dbCtx.Database. ExecuteSqlInterpolatedAsync()方法來執行原生的非查詢SQL語句。
static async Task Main(string[] args)
{using (MyDbContext ctx = new MyDbContext()){string strName = "小劉";await ctx.Database.ExecuteSqlInterpolatedAsync(@$"insert into T_Books(Name,Author,Price)select Name,{strName},Pricefrom T_Books where Price > 20");}
}
有SQL注入漏洞
字符串內插的方式會不會有SQL注入攻擊漏洞嗎?查看一下執行的SQL語句吧。
insert into T_Books(Name,Author,Price)
select Name,@p0,Price
from T_Books where Price > 20
ExecuteSqlInterpolatedAsync?
字符串內插如果賦值給string變量,就是字符串拼接;字符串內插如果賦值給FormattableString變量,編譯器就會構造FormattableString 對象。打印FormattableString的成員試試看。
static async Task Main(string[] args)
{using (MyDbContext ctx = new MyDbContext()){string strName = "小劉";FormattableString sql = (@$"insert into T_Books(Name,Author,Price)select Name,{strName},Pricefrom T_Books where Price > 20");Console.WriteLine("Format:" + sql.Format);Console.WriteLine("參數:" + string.Join(",", sql.GetArguments()));}
}
ExecuteSqlInterpolatedAsync()的參數是FormattableString類型。因此ExecuteSqlInterpolatedAsync會進行參數化SQL的處理。
其他方法
除了ExecuteSqlInterpolated ()、ExecuteSqlInterpolatedAsync() ,還有ExecuteSqlRaw()、ExecuteSqlRawAsync() 也可以執行原生SQL語句,但需要開發人員自己處理查詢參數,因此不推薦使用。
執行實體相關查詢原生SQL語句
FromSqlInterpolated
如果要執行的原生SQL是一個查詢語句,并且查詢的結果也能對應一個實體,就可以調用對應實體的DbSet的FromSqlInterpolated()方法來執行一個查詢SQL語句,同樣使用字符串內插來傳遞參數。
static async Task Main(string[] args)
{using (MyDbContext ctx = new MyDbContext()){string NamePattern = "%計算%";var queryable = ctx.Books.FromSqlInterpolated(@$"select * from T_Books where Name Like {NamePattern} order by newid()");foreach (var item in queryable){Console.WriteLine(item.Id + item.Name);}}
}
select * from T_Books where Name Like @p0 order by newid()
FromSqlInterpolated()方法的返回值是IQueryable類型的,因此我們可以在實際執行IQueryable之前,對IQueryable進行進一步的處理。
把只能用原生SQL語句寫的邏輯用FromSqlInterpolated()去執行,然后把分頁、分組、二次過濾、排序、Include等其他邏輯盡可能仍然使用EF Core的標準操作去實現。
局限性
- SQL 查詢必須返回實體類型對應數據庫表的所有列;
- 結果集中的列名必須與屬性映射到的列名稱匹配。
- 只能單表查詢,不能使用Join語句進行關聯查詢。但是可以在查詢后面使用Include()來進行關聯數據的獲取。
執行任意原生SQL查詢語句
什么時候用ADO.NET
FromSqlInterpolated()只能單表查詢,但是在實現報表查詢等的時候,SQL語句通常是非常復雜的,不僅要多表Join,而且返回的查詢結果一般也都不會和一個實體類完整對應。因此需要一種執行任意SQL查詢語句的機制。
EF Core中允許把視圖或存儲過程映射為實體,因此可以把復雜的查詢語句寫成視圖或存儲過程,然后再聲明對應的實體類,并且在DbContext中配置對應的DbSet。
不推薦寫存儲過程;項目復雜查詢很多,導致:視圖太多;非實體的DbSet;DbSet膨脹。
執行任意SQL
dbCxt.Database.GetDbConnection()獲得ADO.NET Core的數據庫連接對象。
Dapper
推薦用Dapper等框架執行原生復雜查詢SQL。
EF Core和Dapper并不是對立,可以同時使用,EF Core簡單方便
var items = ctx.Database.GetDbConnection().Query<GroupArticleByPrice>("select Price,Count(*) PCount from T_Books group by price");
foreach (var item in items)
{Console.WriteLine($"Price{item.Price},Count{item.PCount}");
}
總結
一般Linq操作就夠了,盡量不用寫原生SQL;
- 非查詢SQL用ExecuteSqlInterpolated () ;
- 針對實體的SQL查詢用FromSqlInterpolated()。
- 復雜SQL查詢用ADO.NET的方式或者Dapper等。