一. 原生SQL查詢
接著上篇講。通過 Entity Framework Core 可以在使用關(guān)系數(shù)據(jù)庫時下降到原始 SQL 查詢。 在無法使用 LINQ 表達要執(zhí)行的查詢時,或因使用 LINQ 查詢而導(dǎo)致低效的 SQL 查詢時非常有用。 原始 SQL 查詢可返回實體類型,或者從 EF Core 2.1 開始,可返回模型中的查詢類型。
1.1 基本的原始SQL查詢
可以使用FromSql擴展方法,基于原始的SQL查詢,開始LINQ查詢。
var blogs = context.Blogs
.FromSql("SELECT * FROM dbo.Blogs")
.ToList();
--通過sql server profiler監(jiān)聽的sql,如下所示:
select * from dbo.blogs
1.2 原生 SQL 查詢可用于執(zhí)行存儲過程(GetMostPopularBlogs)
var blogs = context.Blogs
.FromSql("EXECUTE dbo.GetMostPopularBlogs")
.ToList();
1.3 傳遞參數(shù)
在使用FromSql執(zhí)行原始sql查詢時,傳遞參數(shù)要防止SQL注入攻擊,可以在SQL查詢字符串中包含參數(shù)占位符,然后提供參數(shù)值作為附加參數(shù)。任何參數(shù)值將自動轉(zhuǎn)換為DbParameter來防止SQL注入。
var id = 4;
var blgos= BloggingContext.Blogs.FromSql("select * from dbo.blogs where BlogId={0}",id).ToList();
--通過sql server profiler監(jiān)聽的sql,如下所示:
exec sp_executesql N'select * from dbo.blogs where BlogId=@p0',N'@p0 int',@p0=4
可以構(gòu)造 DbParameter 并將其作為參數(shù)值提供。 這樣可以在 SQL 查詢字符串中使用命名參數(shù)(與上面占位符實現(xiàn)一樣,只不過這里顯示提供了命名參數(shù)@BlogId)。
var user = new SqlParameter("@BlogId", 4);
var blgos= BloggingContext.Blogs.FromSql("select * from dbo.blogs where BlogId={0}", user).ToList();
--通過sql server profiler監(jiān)聽的sql,如下所示:
exec sp_executesql N'select * from dbo.blogs where BlogId=@BlogId',N'@BlogId int',@BlogId=4
1.4 使用 LINQ 編寫
發(fā)送到數(shù)據(jù)庫中的 SQL 查詢可以是組合的,則可以在原始 SQL 查詢后面緊跟著使用 LINQ 運算符。 以 SELECT 關(guān)鍵字開始的 SQL 查詢一般是可組合的。以下示例使用原始SQL查詢,然后使用LINQ對其進行編寫以執(zhí)行過濾和排序。
var blgos= BloggingContext.Blogs
.FromSql("select blogid,url from dbo.blogs")
.Where(b=>b.BlogId>2)
.OrderByDescending(b=>b.BlogId)
.Select(b=> new{ b.BlogId, b.Url})
.ToList();
--通過sql server profiler監(jiān)聽的sql,原始sql成了一個子查詢,如下所示:
SELECT [b].[BlogId], [b].[Url]
FROM (
select blogid,url from dbo.blogs
) AS [b]
WHERE [b].[BlogId] > 2
ORDER BY [b].[BlogId] DESC
二. 異步查詢
當(dāng)在數(shù)據(jù)庫中執(zhí)行查詢時,異步查詢可避免阻止線程。 這有助于避免凍結(jié)富客戶端應(yīng)用程序的 UI。 異步操作還可以增加 Web 應(yīng)用程序的吞吐量,可以在等數(shù)據(jù)庫操作完成時(I/O),釋放當(dāng)前線程到線程池,該線程可去處理其他請求。
注意: EF Core 不支持在同一上下文實例上運行多個并行操作。 應(yīng)始終等待操作完成,然后再開始下一個操作。 這通常是通過在每個異步操作上使用 await 關(guān)鍵字完成的。
Entity Framework Core 提供了一組異步擴展方法,可用作執(zhí)行查詢并返回結(jié)果的 LINQ 方法的替代方法。示例包括 ToListAsync()、ToArrayAsync()、SingleAsync() 等。對于部分 LINQ 運算符(如 Where(...)、OrderBy(...) 等),沒有對應(yīng)的異步版本,因為這些方法僅用于構(gòu)建 LINQ 表達式樹,而未將查詢發(fā)送到數(shù)據(jù)庫中執(zhí)行。
下面是一個示例,注意async必須搭配await,只有await完成后才會釋放當(dāng)前EF上下文, async后面必須是Task(無返回值)或Task<T>(有返回值):
public async Task<List<Blog>> GetBlogsAsync()
{return await context.Blogs.ToListAsync();
}
三. 全局查詢篩選器
全局查詢篩選器是應(yīng)用于元數(shù)據(jù)模型(通常為 OnModelCreating)中的實體類型的 LINQ 查詢謂詞(通常傳遞給 LINQ Where 查詢運算符的布爾表達式)。
下面使用 HasQueryFilter API 在 OnModelCreating 中配置查詢篩選器。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//IsDeleted==1 沒有被邏輯刪除的Blog數(shù)據(jù)
modelBuilder.Entity<Blog>().HasQueryFilter(b=>b.IsDeleted==1);
}
var blgos= BloggingContext.Blogs
.FromSql("select * from dbo.blogs")
.Where(b=>b.BlogId>2)
.OrderByDescending(b=>b.BlogId)
.Select(b=> new{ b.BlogId, b.Url})
.ToList();
--通過sql server profiler監(jiān)聽的sql,where后面加了IsDeleted==1,如下所示:
SELECT [b].[BlogId], [b].[Url]
FROM (
select * from dbo.blogs
) AS [b]
WHERE ([b].[IsDeleted] = 1) AND ([b].[BlogId] > 2)
ORDER BY [b].[BlogId] DESC
//可以在Linq查詢語句中禁用全局查詢篩選器
BloggingContext.Blogs.IgnoreQueryFilters().ToList();
參考文獻: