實體框架高級應用之動態過濾 EntityFramework DynamicFilters

實體框架高級應用之動態過濾 EntityFramework DynamicFilters

咱們開門見山,直奔主題。git

 

1、EntityFramework DynamicFilters 是什麼,它能作什麼?github

  EntityFramework DynamicFilters是一個開源項目。你能夠到這裏去下載它的源碼。顧名思義,它爲咱們作的事,就是幫咱們動態過濾數據。爲了照顧初學者,咱們從頭道來。sql

  一、何爲數據過濾?數據庫

     數據過濾說簡單點,就是去掉咱們不想要的數據。SQL語句中的where從句,Linq中的where從句,還有擴展方法Where,就是完成這件光榮任務的。緩存

 

  二、何爲動態?框架

    動態的意思就是不死板地應用咱們所寫的條件,好比,咱們在一個地方寫了where從句,它只能用於此次查詢,下次遇到類似的狀況時,咱們還得老老實實的寫 where xxx=xxx。很長的一段時間,咱們一直這樣,很和諧地使用着這種方法。忽然有一天,抓了抓頭:若是相似的狀況,能自動加上相應的過慮條件,或是應用相應的規則,該有多好?因而就有動態。固然這裏的動態,只是咱們面對問題的一個方面。ide

 

  三、廢話半天,它到底能作什麼,具體點,好不?函數

    它能夠爲咱們建立全局的,針對實體框架查詢的過慮器,這些過濾器會自動應用於每個查詢。能被用於支持多租戶,軟刪除,等等。過濾器能經過返回布爾類型的Linq表達式來建立,同時還支持Contains()操做符(方法)。目前支持的數據庫有MS SQL Server(包含 Azure),MySql,Oracle。ui

 

2、 沒有它時,咱們是怎麼作的?spa

  咱們以軟刪除(不是真正意義上的刪除數據,只是在相應的記錄上做一個刪除標識)爲例。正由於數據沒有被真正地刪除,只是被咱們用一個標識給標記起來了,那麼,咱們就得在每個查詢的地方加上一個條件(過濾掉標記爲刪除的數據),代碼可能長成這樣:

1  var blogs = context.BlogEntries.Where(b => b.IsDeleted == false).ToList();

上面的代碼就不用多解釋了,相信你能看明白。 若是是sql 語句,你可能會說,這有什麼難的,我找一個地方,把全部的查詢拼接上這個條件不就OK。 確實如此,但,這裏只是拿這個簡單的場景來做爲示例,複雜的場景呢?其次,Linq表達式拼接條件 ,不是像字符串那樣爲所欲爲,至少很大一部分人是這樣,固然也包含我。每個查詢都手工加上這樣的條件,不光是工做量增長了,可維護性下降了,還分散了咱們的核心業務邏輯的注意力。

 

3、EntityFramework DynamicFilters給咱們帶來了改變

  固然,它只是衆多解決方案之一,只是做者無私的分享出來了,沒把它當成寶供在本身的電腦裏。 我只須要在上下文DbContext的OnModelCreating 方法中添加過濾器。代碼以下:

 1     protected override void OnModelCreating(DbModelBuilder modelBuilder)
 2         {
 3             base.OnModelCreating(modelBuilder);
 4 
 5             //限制全部針對BlogEntry查詢的過慮(只獲取未刪除的)
 6             //這裏的全局過慮,使用了委託,以便在每次須要計算值
 7             //重要:若是值使用的是一個委託,請確保它在你的應用中是全局的,
 8             modelBuilder.Filter("BlogEntryFilter", (BlogEntry b, bool isDeleted) => (b.IsDeleted == isDeleted),
 9                                                 () => false);
10 
11         }

就這樣,它就會在咱們每個關於Blog實體的查詢中添加上條件(b => b.IsDeleted == false)。咱們無需關心它如何添加這個條件,使用的地方,徹底透明,就像沒有它同樣。示例代碼以下:

 1          /// <summary>
 2         /// 查詢
 3         /// </summary>
 4         /// <param name="context"></param>
 5         /// <param name="userName"></param>
 6         private static void Query(ExampleContext context, string userName)
 7         {
 8             var account = context.Accounts
 9                 .Include(a => a.BlogEntries).FirstOrDefault(a => a.UserName == userName);
13 Console.WriteLine("帳號{0}的博客有:",userName); 14 if (account == null) return; 15 foreach (var blog in account.BlogEntries) 16 { 17 Console.WriteLine("{0}",blog.Id); 18 } 19 }

但須要注意的是,若是在同一個上下文DbContext實例中,運用過慮器以前,過慮器有被禁用過,而數據被緩存時,過濾器就不會起任何效果,全部使用時,你必定要避免在同一個上下文中因更改過濾器而影響結果的狀況。

若是你在某種狀況下不想使用過慮器時,你可使用以下代碼將其禁用:

1             //禁用過濾器
2             context.DisableFilter("BlogEntryFilter");    

 

注意:禁用只對當前上下文DbContext實例有效,不影響別的上下文實例。若是你想對全部的上下文實例有效時,能夠在 OnModelCreating方法中使用全局禁用函數:

1 //全局禁用過濾器
2 modelBuilder.DisableFilterGlobally("BlogEntryFilter");

 

啓用的代碼相似,這裏就很少少了,直接看代碼:

1             //啓用過濾器
2             context.EnableFilter("BlogEntryFilter");
3             context.EnableAllFilters();

 

說了這麼多,咱們來看看運用過濾器的效果吧,代碼以下:

 1  class Program {
 2         static void Main(string[] args) {
 3 
 4             // 過濾器默認啓用
 5             var context = new ExampleContext();
 6 
 7             Console.WriteLine(" 使用過濾器BlogEntryFilter進行查詢");
 8             Query(context, "homer");
 9 
10             //禁用過濾器
11             context.DisableFilter("BlogEntryFilter");
12             
13             Console.WriteLine(" 禁用過濾器BlogEntryFilter進行查詢");
14             Query(context, "homer");
15 
16             Console.ReadLine();
17         }
18 
19         /// <summary>
20         /// 查詢
21         /// </summary>
22         /// <param name="context"></param>
23         /// <param name="userName"></param>
24         private static void Query(ExampleContext context, string userName)
25         {
26             var account = context.Accounts
27                 .Include(a => a.BlogEntries).FirstOrDefault(a => a.UserName == userName);
28 
29             Console.WriteLine("帳號{0}的博客有:",userName);
30             if (account == null) return;
31             foreach (var blog in account.BlogEntries)
32             {
33                 Console.WriteLine("{0}",blog.Id);
34             }
35         }
36     }

 

代碼輸出以下:

 

4、EntityFramework DynamicFilters原理概述

  它是經過在對象 DbModelBuilder 上添加擴展方法Filter實現的,核心代碼以下:

 1   private static void Filter<TEntity>(DbModelBuilder modelBuilder, string filterName, LambdaExpression predicate, params object[] valueList)
 2         {
 3             InitializeDynamicFilters(null);
 4 
 5             filterName = ScrubFilterName(filterName);
 6 
 7             modelBuilder.Conventions.Add(new DynamicFilterConvention(filterName, typeof(TEntity), predicate));
 8 
 9             //  Always add the filter to _GlobalParameterValues - need it to be able to disable it
10             _GlobalParameterValues.TryAdd(filterName, new DynamicFilterParameters());
11 
12             int numParams = predicate.Parameters == null ? 0 : predicate.Parameters.Count;
13             int numValues = valueList == null ? 0 : valueList.Length;
14             for (int i = 1; i < numParams; i++)
15             {
16                 object value = ((i - 1) < numValues) ? valueList[i - 1] : null;
17                 SetFilterGlobalParameterValue(null, filterName, predicate.Parameters[i].Name, value);
18             }
19         }

 

整個項目的源代碼很少,若是你有興趣,請閱讀源代碼。文中使用的代碼,請於結尾處下載。

最後,我要說明的是,文中並無把 EntityFramework DynamicFilters的方方面面說完,只是說了一些常見的場景。更多的細節,說閱讀源碼,或者和你們一塊兒實踐、交流。

 

文中示例源代碼下載地址:http://files.cnblogs.com/files/VolcanoCloud/EFDynamicFilterDemo.rar

 

實體框架交流QQ羣:  458326058,歡迎有興趣的朋友加入一塊兒交流

謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/VolcanoCloud/

相關文章
相關標籤/搜索