EntityFramework Core 2.0自定义标量函数两种方式
前言
上一節我們講完原始查詢如何防止SQL注入問題同時并提供了幾種方式。本節我們繼續來講講EF Core 2.0中的新特性自定義標量函數。
自定義標量函數兩種方式
在EF Core 2.0中我們可以將方法映射到數據庫中的標量函數,我們可在LINQ中調用此方法并會被正確翻譯成SQL語句,這為編寫數據訪問層的開發人員提供了一個很棒的功能來創建一個方法并在其上應用DbFunction特性即可。該屬性會將靜態CLR方法映射到數據庫函數,以便可以在LINQ查詢中使用此方法。默認情況下,數據庫函數中的CLR靜態方法名稱必須相同,除非我們在DbFunctionAttribute中指定了不同的名稱。自定義標量函數必須滿足如下兩個條件。
?(1)函數必須是靜態方法且在上下文中聲明。
?(2)只能作為參數標量值返回。
?自定義標量函數方式一
?我們可直接在上下文中定義一個靜態方法,如下:
[DbFunction]public static string ScalarFunction(string name){throw new NotImplementedException();}?自定義標量函數方式二
public static string ScalarFunction(string name){throw new NotImplementedException();}然后在OnModelCreating方法利用ModelBuilder中的HasDbFunction來調用上述方法,如下:
modelBuilder.HasDbFunction(() => ScalarFunction(null));請注意以上自定義標量函數的兩種方式必須定義架構名稱即Schema,否則在調用上述方法查詢時將拋出【System.Data.SqlClient.SqlException:“'您自定義的函數名稱' 不是可以識別的 內置函數名稱。”】,也就是說我們無論是利用DbFunction特性還是HasDbFunction方法映射自定義標量函數也好都必須指定Schema,我們默認指定為dbo,如下:
[DbFunction(FunctionName = "UdfFunction", Schema = "dbo")]public static string ScalarFunction(string name){throw new NotImplementedException();}或者
public static string ScalarFunction(string name){throw new NotImplementedException();}modelBuilder.HasDbFunction(() => ScalarFunction(null)).HasName("UdfFunction").HasSchema("dbo");或者modelBuilder.HasDbFunction(GetType().GetMethod("ScalarFunction"), options =>{options.HasName("UdfFunction");options.HasSchema("dbo");});上述講解了在EF Core 2.0中如何創建標量函數,講了這么多,到底怎么用,或者說它的出現可以解決什么問題呢?下面我們首先來看一個例子。比如我們想查詢每篇博客的評論數的均值,接下來我們會進行如下查詢:
using (var context = new EFCoreDbContext()){var blogs = context.Blogs.AsNoTracking();var result = blogs.Select(b => new BlogDTO(){Id = b.Id,Name = b.Name,Count = b.Posts.Count() > 0 ? b.Posts.Average(d => d.CommentCount) : 0}).ToList();}此時將出現函數Average無法翻譯成SQL,只能在內存中進行查詢。在EF Core中如果您有詳細查看過生成的SQL語句的話,您就能夠明白,對于Min、Max、Average等LINQ函數,EF Core不支持翻譯成遠程SQL,只能在本地查詢。此時我們再來看看進行此次查詢總共耗時100ms,如下:
接下來我們再利用自定義標量函數查詢試試。首先定義標量函數
public static double? UdfAverage(int blogId){throw new Exception();} modelBuilder.HasDbFunction(() => UdfAverage(default(int))).HasSchema("dbo");然后我們再來創建標量函數
public static class AddUdfHelper{public static void AddUdfToDatabase(this DbContext context){using (var transaction = context.Database.BeginTransaction()){try{context.Database.ExecuteSqlCommand("IF OBJECT_ID('dbo.UdfAverage', N'FN') IS NOT NULL " +"DROP FUNCTION dbo.UdfAverage");context.Database.ExecuteSqlCommand("CREATE FUNCTION UdfAverage (@blogId int)" +@" RETURNS FLOATASBEGINDECLARE @result AS FLOATSELECT @result = AVG(CAST([CommentCount] AS FLOAT)) FROM dbo.Posts AS pWHERE p.BlogId = @blogIdRETURN @resultEND");transaction.Commit();}catch (Exception ex){throw ex;}}}}上述標量函數理應在遷移時生成,現在我們首先在上下文構造函數中創建即在運行時創建。在數據庫中函數中的標量函數中將生成UdfAverage函數,如下:
接下來我們再來調用創建的自定義標量函數,如下:
using (var context = new EFCoreDbContext()){var blogs = context.Blogs.AsNoTracking();var result = blogs.Select(b => new BlogDTO(){Id = b.Id,Name = b.Name,Count = EFCoreDbContext.UdfAverage(b.Id)}).ToList();}我們看看此此查詢總共耗時77ms。相比上述未調用標量函數直接調用Average方法,不會翻譯成SQL,所以在數據庫中查詢一次,然后加載到內存中再查詢一次,效果顯而易見。
總結?
本節我們詳細講解了EF Core ?2.0中的自定義標量函數,若我們需要進行子查詢返回標量值時此時創建自定義標量函數將成為首選,其性能比調用內置的APi然后在內存中進行查詢而不會翻譯成SQL的性能更好。精簡的內容,簡單的講解,希望對閱讀的您有所幫助,我們明天再會。
總結
以上是生活随笔為你收集整理的EntityFramework Core 2.0自定义标量函数两种方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第179天:javascript中rep
- 下一篇: 11.10/11.11/11.12 安装