<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      happyhippy

      這個世界的問題在于聰明人充滿疑惑,而傻子們堅信不疑。--羅素

          如果允許在UI層直接訪問Linq to Sql的DataContext,可以省去很多問題,譬如在處理多表join的時候,我們使用var來定義L2S查詢,讓編譯器自動推斷變量的具體類型(IQueryable<匿名類型>),并提供友好的智能提示;而且可以充分應用L2S的延遲加載特性,來進行動態查詢。但如果我們希望將業務邏輯放在一個獨立的層中(譬如封裝在遠程的WCF應用中),又希望在邏輯層應用Linq to sql,則情況就比較復雜了;由于我們只能使用var(IQueryable<匿名類型>),而var只能定義方法(Method)范圍中聲明的變量,出了方法(Method)之后IDE就不認得它了;在這種對IQueryable<匿名類型>一無所知的情況下,又希望能在開發時也能應用上IDE的智能感應,我們該怎么定義層之間交互的數據傳輸載體呢?又如何對它進行動態查詢呢?

           內容比較多,分上下兩篇,上篇了介紹查詢返回自定義實體,本篇介紹動態查詢。

       

           在我們的日常開發中,時常需要根據用戶在UI上的輸入來進行動態查詢。在Ado.Net主宰的舊石器時代,一般會這樣來動態拼接SQL查詢條件:

         1: string filter = " 1=1";
         2: if(XXOO文本框不為空)
         3:     filter += string.Format(" AND XXOO='{0}', XXOO)";
         4: if(OOXX文本框不為空)
         5:     filter += string.Format(" AND OOXX='{0}', OOXX)";
         6: gridView.DataSource = BusinessLogic.XOXOQuery(filter);

          然后將過濾條件傳給業務邏輯層,由業務邏輯層拼接出完整的TSQL語句。  但到了LINQ to SQL時代,我們該辦了呢?還要繼續玩字符串拼接游戲嗎?

          后面將以NorthWind為例,動態查詢產品(Product)及其供應商信息(Supplier):L2S_DynamicQuery

         1: partial class ProductExt : Products
         2: {
         3:     public string CompanyName { get; set; }
         4: }

       

      1. UI層直接訪問DataContext

          如果使用L2S查詢延遲加載的特性,動態查詢也變得相當簡單:

         1: public void TestDynamicQuery()
         2: {
         3:     using (NorthWindDataContext context = new NorthWindDataContext())
         4:     {
         5:         var query = from P in context.Products
         6:                            join S in context.Suppliers
         7:                             on P.SupplierID equals S.SupplierID
         8:                            select new
         9:                            {
        10:                                P.ProductName,
        11:                                P.UnitPrice,
        12:                                P.QuantityPerUnit,
        13:                                S.CompanyName
        14:                            };
        15:         if(XXOO)
        16:             query = query.Where(p => p.ProductName.Contains("che"));
        17:         if(OOXX)
        18:             query = query.Where(p => p.UnitPrice >= 20);
        19:         gridView.DataSource = query.ToList(); //延遲加載,ToList時才進行運算
        20:         gridView.DataBind();
        21:     }
        22: }

          看起來還是比較舒服的,不用再繼續拼接SQL了,開發時也可以充分利用IDE的智能感應。

          但也不是無可挑剔,這里的邏輯無法復用。假如另外一個應用場景,要根據供應商名稱來查詢產品信息,我們該怎么處理呢,另外再寫一個查詢?如果再多一個引用場景呢,難道我們每次都要Ctrl+C | Ctrl +V?還是把這個邏輯封裝在業務邏輯層,讓多個的頁面都可以使用?

       

      2. 分層后引發的問題

          分層的好處之一就是邏輯復用。在Ado.Net時代,我們可以把這個join操作放在業務邏輯層,UI層只需要根據不同的應用場景,拼接where條件,然后傳給業務邏輯層處理即可。

          當在分層應用中使用L2S時,如果想把這個邏輯放到業務邏輯層,我們或許可以這樣做:

      2.1.  繼續拼接

          或許我們想過繼續按照舊石器時代的做法,直接拼接;但是我們立刻會發現顯然是行不通的,我們無法“直接”將L2S查詢與字符串進行拼接

      2.2. 構造Expression或者Func

          query.Where()可以接受一個表達式Expression<Func<TSource, bool> predicate>或者委托Func<TSource, bool> predicate,或許我們想過嘗試構造這樣的Expression或者Func;但是我們又會遇到新的問題,如上面的查詢,我們的query的類型是IQueryable<匿名類型>,匿名類型的定義是在編譯階段才由編譯器創建的,開發時我們根本不知道TSource是類型,又該怎么創建這樣的Expression或者Func呢

       

      3. 使用Dynamic LINQ繼續拼接游戲

          上面2.1中提到無法“直接”將L2S查詢與字符串進行拼接,但是可以通過一些擴展來間接達到目的,網上已經有人這么做了,具體可以參考:Dynamic LINQ。下面是一個示例:

         1: Northwind db = new Northwind(connString); 
         2: var query =
         3:     db.Customers.Where("City == @0 and Orders.Count >= @1", "London", 10).
         4:     OrderBy("CompanyName").
         5:     Select("New(CompanyName as Name, Phone)");

          看起來,貌似我們又可以繼續玩字符串拼接了。不過需要注意的一點兒是,這里拼接的字符串不再是TSQL中的字符串命令了,而是L2S查詢。這是基于如下原因:在L2S中,查詢被表示為一個表達式目錄樹(Expression Tree,表示的是數據,不是代碼),待需要訪問查詢結果集時(針對延遲加載的情況),這棵樹才被對應的Provider(這里用的是SQL Server,所以對應的是SqlProvider)翻譯為TSQL,并發送給ADO.Net來執行;Dynamic LINQ就是將傳進來的字符串解析為表達式目錄樹,并與原來的L2S進行適當地合并,從而得到最終的表達式目錄樹。

          根據字符串進行拼接,是一種解決辦法。但是這樣做有個不好的地方,就是我們失去了IDE的智能感應。

       

      4. 對IQueryable進行動態查詢擴展

          上面2.2節中,還提到了另外一種處理思路,那就是構造Expression或者Func;當然,這里會遇到上面提到的問題:我們的query的類型是IQueryable<匿名類型>,開發時根本不知道其具體類型,如何創建Expression<Func<匿名類型, bool> predicate>或者委托Func<匿名類型, bool> predicate呢

         下面是我實現過程中的那艱苦卓越的辛酸歷程:

          還是拿上面的查詢作為例子,譬如要查詢ProductName.Contains("che")) && UnitPrice >= 20的記錄;則我們能構造出來的需要構造出來的表達式會是什么樣子呢?下面是兩者之間的差距:

      Expression<Func<Products, bool>> predicate = t => t.ProductName.Contains("che") && t.UnitPrice >= 22 //Can Do
      Expression<Func<匿名類型, bool>> predicate = t => t.ProductName.Contains("che") && t.UnitPrice >= 22 //To DO

          差距呢?乍一看,這就是一對雙胞胎啊,還需要轉換個啥子,吃飽撐的啊……
          不過細看之后,二者確有不同之處,下面是補全后的對比:

      Expression<Func<Products, bool>> predicate = (Products  t) => t.ProductName.Contains("che") && t.UnitPrice >= 22
      Expression<Func<匿名類型, bool>> predicate = (匿名類型 t) => t.ProductName.Contains("che") && t.UnitPrice >= 22

          現在可以看到,這不是一對普通的雙胞胎,基因中的軟色體都不是一個樣子,這是一對龍鳳胎。由于.Net是強類型語言,IEnumerable<TSource>.Where()方法只認得后者,而拒絕接受前者,因此接下來,我們的目標是……沒有蛀牙?NO,基因手術……當然,也希望手術的副作用包括沒有蛀牙(bug)。

       

                                            ------------------我是華麗的分割線(happyhippy.cnblogs.com)--------------------

       

          上一篇中,我實現了一個對象轉換器,可以把一個對象轉換成另一個對象;但這里用不上,這里需要換的是基因,需要把一種類型換成另一種類型。所以需要急切實現的一個函數就是,能把一個LambdaExpression的參數類型換成另一種類型,于是我實現了下面的方法:(其中,TSource為源類型,TResult為目標類型)

      public static Expression<Func<TResult, bool>> Replace<TSource, TResult>( 
          this Expression<Func<TSource, bool>> predicate)
      {
          ParameterConverter pc = new ParameterConverter(predicate);
          return (Expression<Func<TResult, bool>>)pc.Replace(typeof(TResult));
      }

          在開始寫這段代碼之前,我的表達式目錄樹知識幾乎為0;于是又開始翻MSDN,找到了這里:LINQ 中的表達式目錄樹……最終在MSDN的幫助下,我終于把它給實現出來了,完成后我不禁沾沾自喜(雖然只有幾十行代碼,可行代碼不到十來行,剩下的是從MSDN中的ExpressionVisitor盜版的,但也耗了我整整一個半天)……

          古人云:樂極生悲。看來這句話還是有道理的。慶幸之后,接著我又墜入了萬丈深淵,因為我不知道怎么調用這個方法!在這個方法外部,我們的query是IQueryable<匿名類型>,在IQueryable<匿名類型>.Where()方法中,嘗試調用這個Replace方法的時候,我不知道該傳什么類型參數給TResult。我又白干了……

       

          有時候,看起來只有一步之遙,但其實天各一方……

                                            ------------------我是可恥的分割線(happyhippy.cnblogs.com)--------------------

          有時候,看起來貌似遙不可及,但其實近在咫尺……

       

          前面提到:在L2S中,查詢被表示為一個表達式目錄樹(Expression Tree,表示的是數據,不是代碼)。我既然可以向上面這樣修改Expression<Func<TSource, bool>>,那我應該也可以修改這個這個LINQ查詢,而且Dynamic LINQ也正是在修改LINQ查詢啊。

          看了下Dynamic LINQ中對Where的擴展,才知道IQueryable公布了其Provider屬性:IQueryable.Provider,我們可可以直接調用Provider.CreateQuery來對原有的query進行擴充:

      Expression<Func<Products, bool>> predicate = t => t.ProductName.Contains("che") && t.UnitPrice >= 22;
      IQueryable query = from P in context.Products
                         join S in context.Suppliers
                         on P.SupplierID equals S.SupplierID
                         select new
                         {
                             P.ProductName,
                             P.UnitPrice,
                             P.QuantityPerUnit,
                             S.CompanyName
                         };
      query = query.Provider.CreateQuery(
          Expression.Call(
              typeof(Queryable), "Where",
              new Type[] { query.ElementType }, 
              query.Expression, predicate.Replace<Products>(query.ElementType)));

          前面,我無法完成將TSource(Products)強制轉換成匿名類型;但這里,通過構造Expression,來將類型弱化,最終將通過Expression的編譯和執行功能,來實現這種轉換。

          當然,每次這樣寫的話,我也覺得麻煩;于是,就有了下面的對IQueryable的擴展:

      public static IQueryable DynamicWhere<T>(this IQueryable query, Expression<Func<T, bool>> predicate)
      {
          if (predicate == null)
              return query;
       
          return query.Provider.CreateQuery(
              Expression.Call(
                  typeof(Queryable), "Where",
                  new Type[] { query.ElementType },
                  query.Expression, predicate.Replace<T>(query.ElementType)));
      }
       
      //然后就可以這樣用了:
      public List<ProductExt> TestDynamicQuery3()
      {
          using (NorthWindDataContext context = new NorthWindDataContext())
          {
              IQueryable query = from P in context.Products
                                 join S in context.Suppliers
                                  on P.SupplierID equals S.SupplierID
                                 select new
                                 {
                                     P.ProductName,
                                     P.UnitPrice,
                                     P.QuantityPerUnit,
                                     S.CompanyName,
                                     S.Address
                                 };
              query = query.DynamicWhere((Products p) => p.ProductName.Contains("che"))
                  .DynamicWhere((Suppliers s) => s.Address == "P.O. Box 78934") 
                  //.DynamicWhere((ProductExt p) => p.CompanyName == p.ProductName) //BinaryExpression右邊不能有對參數的引用
                  .DynamicWhere((Products p) => p.UnitPrice >= 5 * 4 + 2);
       
              return query.ConvertTo<ProductExt>();
          }
      }

          由于Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)已經被可恥地占去了,所以這里我定義了一個自己的方法名:DynamicWhere。

                                            ------------------又可恥了一次的分割線(happyhippy.cnblogs.com)--------------------

          最后,來說說這種方法的不足之處:
          (1). 由于我們在Expression<Func<TSource, bool>> predicate時,使用的源類型TSource與query中元素類型(匿名類型)之間的屬性集可能存在不同,因此這里的Expression中,只能使用匿名類型中已經聲明的屬性,使用不屬于該匿名類型的屬性時,編譯時不會抱錯,但運行時會出錯。例如,我還補充傳入了一個根據供應商所在城市的過濾條件:.DynamicWhere((Suppliers s) => s.City== "London"),運行時就掛了……這就又遇到上一節中同樣的問題:UI層怎么知道屬性可用,哪些屬性被閹割了呢?這又是一個問題,暫時只能說:源代碼前沒有秘密。
          (2). BinaryExpression中的右側表達式不能包括對參數的應用。譬如上面代碼中注釋掉的一行,引用了參數p,執行會報錯;這是因為在處理類型參數轉換時,我對BinaryExpressio中的右側表達式CallExpression中的參數表達式進行了運算,轉得到常量表達式。應該還有更好的思路,判斷這些Expresion是否引用了參數,如果引用了參數,則不進行運算,如果沒有引用參數,則進行運算。但是我還沒有考慮出來該怎么來判斷……于是就成了這個樣子。不過對于動態查詢來說,一般情況下應該夠用了,以后想到更好的思路再加進去。

       

      5. 如何進行邏輯復用

          為了將思路描述清楚,前面我只介紹了如何進行動態查詢,而刻意避開了一個問題,就是如何進行邏輯復用。問題要分解開來,然后再逐個擊破~

      5.1 UI與業務邏輯層位于同一地址空間(同一個應用程序域)

          既然位于同一地址空間,那就可以在UI層創建Expression<Func<TSource, bool>> predicate,然后傳入業務邏輯層:

      public List<ProductExt> TestDynamicQuery(Expression<Func<ProductExt, bool>> predicate)
      {
          using (NorthWindDataContext context = new NorthWindDataContext())
          {
              IQueryable query = from P in context.Products
                                 join S in context.Suppliers
                                  on P.SupplierID equals S.SupplierID
                                 select new
                                 {
                                     P.ProductName,
                                     P.UnitPrice,
                                     P.QuantityPerUnit,
                                     S.CompanyName
                                 };
              return query.DynamicWhere(predicate).ConvertTo<ProductExt>();
          }
      }
      //不同場景下的應用:
      //場景1
      Expression<Func<ProductExt, bool>> predicate = t => t.ProductName.Contains("che") && t.UnitPrice >= 22;
      return TestDynamicQuery2(predicate);
      //場景2
      Expression<Func<ProductExt, bool>> predicate = t => t.CompanyName == "New Orleans Cajun Delights";
      return TestDynamicQuery2(predicate);

           但是這樣又引入了新的問題,如何根據用戶的輸入條件,動態構造這個呢? t => t.ProductName.Contains("che") && t.UnitPrice >= 22;

          雖然可以像下面5.2一樣來處理,但是也還是有點兒麻煩;理想情況下,我希望可以像下面這樣來構造predicate,這樣,我們就可以使用&、| 、&=、|=來任意拼接過濾條件了:

         1: Expression<Func<ProductExt, bool>> predicate = null;
         2: predicate &= (t => t.ProductName.Contains("che")) | (t => t.UnitPrice >= 22);

       

      5.2 UI與業務邏輯層位于不同地址空間(跨應用程序域)

          如果UI與業務邏輯位于不同的地址空間,Expression<Func<TSource, bool>> predicate就沒有辦法跨進程傳遞。

          一個可選辦法是,將各個查詢條件值作為參數(如果參數較多的話,或者經常變化的話,可以引入參數對象,具體可參考《重構》),傳到業務邏輯然后再構造Expression。如果您有好的思路,歡迎一起交流。

         1: NorthWindDataContext context = new NorthWindDataContext();
         2:  
         3: public List<ProductExt> TestDynamicQuery(string productName, decimal? unitPrice)
         4: {
         5:     IQueryable query = TestDynamicQueryAll(); //開放了IQueryable,延遲加載
         6:     if (!string.IsNullOrEmpty(productName))
         7:         query = query.DynamicWhere((ProductExt p) => p.ProductName.Contains(productName));
         8:     if (unitPrice.HasValue)
         9:         query = query.DynamicWhere<ProductExt>(p => p.UnitPrice >= unitPrice);
        10:  
        11:     return query.ConvertTo<ProductExt>();
        12: }
        13:  
        14: protected IQueryable TestDynamicQueryAll()
        15: {
        16:     IQueryable query = from P in context.Products
        17:                        join S in context.Suppliers
        18:                         on P.SupplierID equals S.SupplierID
        19:                        select new   //匿名類型
        20:                        {
        21:                            P.ProductName,
        22:                            P.UnitPrice,
        23:                            P.QuantityPerUnit,
        24:                            S.CompanyName
        25:                        };
        26:     return query;  //延遲加載
        27: }

          如果允許IQueryable滿天飛的話,就沒有5.1中提到的動態構造Expression的麻煩問題了。但是貌似看起來還是有點兒煩,能不能了個繼續偷懶呢?

       

      6. 上代碼

          對IQueryable的DynamicWhere擴展,及對Expression<Func<TSource, bool>>的Replace擴展:Linq2SqlExtension.rar

      posted on 2010-01-26 20:01  Silent Void  閱讀(7411)  評論(14)    收藏  舉報

      主站蜘蛛池模板: 扒开双腿猛进入喷水高潮叫声| 成人免费无码大片a毛片| 精品中文字幕一区在线| 国产精品人妻熟女男人的天堂| 日日噜久久人妻一区二区| 无码乱人伦一区二区亚洲一| 久久精品免视看国产成人| 国产精品一区二区三区黄| 72种姿势欧美久久久久大黄蕉| 一区二区三区精品偷拍| 高潮潮喷奶水飞溅视频无码| 久久伊99综合婷婷久久伊| 日本高清在线播放一区二区三区| 99久久亚洲综合精品成人| 亚洲高潮喷水无码AV电影| 亚洲综合色婷婷中文字幕| 2021国产成人精品久久| 龙州县| 亚洲青青草视频在线播放| 亚洲男人的天堂在线观看| 国产私拍福利精品视频| 亚洲国产成人久久综合野外 | 亚洲精品久久麻豆蜜桃| 2020国产欧洲精品网站| 美腿丝袜亚洲综合第一页| 国产人免费人成免费视频| 亚洲精品综合一区二区在线| 88国产精品视频一区二区三区| 色综合热无码热国产| 97欧美精品系列一区二区| 99精品国产一区二区三区不卡 | 亚欧洲乱码视频在线专区| 亚洲欧美不卡高清在线| 中文字幕久区久久中文字幕| 亚洲综合小说另类图片五月天| 日本国产精品第一页久久| 亚洲影院丰满少妇中文字幕无码| 一区二区三区av天堂| 欧美肥老太牲交大战| 免费国产一级特黄aa大片在线| 忘忧草在线社区www中国中文|