咱們開門見山,直奔主題。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/