1. 程式人生 > >linq to sql中慎用Where(Func predicate),小心被Linq給"騙"了!

linq to sql中慎用Where(Func predicate),小心被Linq給"騙"了!

近日在一個大型Web專案中,採用Linq to Sql替換原來的sqlcommand/sqldatareader方式來獲取資料,上線後剛開始一切正常,但是隨著訪問量的增加,網站明顯慢了很多,監測伺服器CPU佔用率/記憶體使用情況等效能指標卻發現均在正常範圍內,無意中在SqlServer Profier中跟蹤資料庫執行的sql語句時,發現有大量語句直接將整個表的資料全部提取出來了,而非僅返回分頁中的當前頁資料!

而這些SQL都是Linq自動翻譯並最終提交到資料庫的,查看了相關的程式碼,明明寫著Skip(n).Take(m)類似的語句,為何還會生成這麼“傻”的sql呢?

於是寫了以下測試程式碼[測試環境:vs.net2008 + sqlsever2005 + win2003],最終發現是Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);使用後,導致這個問題的產生


1.測試表T_Test:

CREATETABLE[dbo].[T_Test](
    
[F_ID][int]IDENTITY(1,1NOTNULL,
    
[F_Name][nvarchar](50) COLLATE Chinese_PRC_CI_AS NULL,
    
[F_Age][int]NULL,
 
CONSTRAINT[PK_T_Test]PRIMARYKEYCLUSTERED 
(
    
[F_ID]ASC
)
WITH (IGNORE_DUP_KEY =OFFON[PRIMARY]
ON[PRIMARY]
錄入了幾條測試資料:
F_ID F_Name F_Age
15 Jimmy 20
16 Mary 14
17 Jack 30
18 張三 35
19 李四 24

2.
新建一個"控制檯應用程式",把T_Test拖到dbml中,Program.cs檔案中輸入如下程式碼:
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Linq.Expressions;
 5using CNTVS.LINQ;
 6
 7namespace TestLinq
 8ExpandedBlockStart.gif{
 9    class Program
10ExpandedSubBlockStart.gif    {
11        staticvoid Main(string[] args)
12ExpandedSubBlockStart.gif        {           
13
14            //Expression<Func<T_Test, bool>> _Expression = PredicateBuilder.True<T_Test>();
15            //_Expression = _Expression.And(t => t.F_Age >= 20).And(t => t.F_Name.Contains("J"));
16            //var Data = GetData(_Expression, 1, 1);1718            var Data = GetData(11);
19
20            ShowData(Data);
21            
22           
23        }
24
25ExpandedSubBlockStart.gif        ///<summary>26        /// 用Where<T>(Expression)方式獲取資料
27        ///</summary>28        ///<param name="ExpWhere"></param>29        ///<param name="PageSize"></param>30        ///<param name="CurrentPageIndex"></param>31        ///<returns></returns>

32static List<T_Test> GetData(Expression<Func<T_Test,bool>> ExpWhere,int PageSize,int CurrentPageIndex) 
33ExpandedSubBlockStart.gif        {
34            List<T_Test> _Result =null;
35            using (DBDataContext db =new DBDataContext())
36ExpandedSubBlockStart.gif            {
37                try38ExpandedSubBlockStart.gif                {
39                    var query = db.T_Test.Where<T_Test>(ExpWhere.Compile()).Skip((CurrentPageIndex -1* PageSize).Take(PageSize);
40                    _Result = query.ToList();
41                }
42ExpandedSubBlockStart.gif                finally{ db.Connection.Close(); }43            }
44            return _Result;
45        }
46
47
48ExpandedSubBlockStart.gif        ///<summary>49        /// 用Where(Lambda)方式獲取資料
50        ///</summary>51        ///<param name="PageSize"></param>52        ///<param name="CurrentPageIndex"></param>53        ///<returns></returns>

54static List<T_Test> GetData(int PageSize, int CurrentPageIndex)
55ExpandedSubBlockStart.gif        {
56            List<T_Test> _Result =null;
57            using (DBDataContext db =new DBDataContext())
58ExpandedSubBlockStart.gif            {
59                try60ExpandedSubBlockStart.gif                {
61                    var query = db.T_Test.Where(t => t.F_Age >=20&& t.F_Name.Contains("J")).Skip((CurrentPageIndex -1* PageSize).Take(PageSize);
62                    _Result = query.ToList();
63                }
64ExpandedSubBlockStart.gif                finally{ db.Connection.Close(); }65            }
66            return _Result;
67        }
68        
69
70ExpandedSubBlockStart.gif        ///<summary>71        /// 顯示資料
72        ///</summary>73        ///<param name="Data"></param>

74staticvoid ShowData(List<T_Test> Data) 
75ExpandedSubBlockStart.gif        {
76            foreach (var item in Data)
77ExpandedSubBlockStart.gif            {
78                Console.WriteLine("Name:{0}/t,Age:{1}", item.F_Name, item.F_Age.ToString());
79            }
80            Console.ReadKey();
81        }
82    }
83}

程式碼很簡單,找出F_Name中包含字母"J",F_Age大於20的記錄,並且跳過第一個後,僅獲取一條記錄

注:PredicateBuilder是一個老外寫的用於動態構造Expression表示式的工具類,在查詢條件不確定,需要動態建立時,非常有用,完整程式碼如下:


ContractedBlock.gifCode

以下是輸出結果:

Name:Jimmy      ,Age:20


用Sql Server Profiler跟蹤提交到資料庫的語句為:

exec sp_executesql N'SELECT [t1].[F_ID], [t1].[F_Name], [t1].[F_Age]
FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY [t0].[F_ID], [t0].[F_Name], [t0].[F_Age]) AS [ROW_NUMBER], [t0].[F_ID], [t0].[F_Name], [t0].[F_Age]

相關推薦

linq to sql慎用Where(Func predicate)小心Linq""!

近日在一個大型Web專案中,採用Linq to Sql替換原來的sqlcommand/sqldatareader方式來獲取資料,上線後剛開始一切正常,但是隨著訪問量的增加,網站明顯慢了很多,監測伺服器CPU佔用率/記憶體使用情況等效能指標卻發現均在正常範圍內,無意中在SqlS

Linq TO Entity Or Linq TO EF where引數化查詢

using System; using System.Linq.Expressions; namespace Utils { /// <summary> /// 統一ParameterExpression /// </summary>

Linq to sql使用DateDiff()

 Linq to sql中使用DateDiff() 計算時間差的方法 第一種辦法: from p in PurchaseLists where EntityFunctions.DiffDays(p.CreateTime,DateTime.Now) >=(p.DayLen/2) selec

關於Linq to Sql 的left join defaultifempty的相關注意事項

            var q = (from c in                          (from a1 in db.StoreIns                           group a1 by a1.StoreNum into g                 

Linq to SQL的模糊查詢

一、 query = from c in tbs where c.sx != "類別" && c.path_tbname.Contains(tName.Text) orderby c.id se

LINQ : 在LINQ TO SQL使用事務和控制併發

LINQ TO SQL,顧名思義,涉及到了資料庫操作。那麼就會有一個事務的概念,例如,假設我們需要一次性插入兩個實體,但希望保證這兩個操作的完整性的話。 1. 隱式事務 其實,即便我們不寫程式碼,LINQ TO SQL也會自動建立一個事務的。預設事務隔離級別為ReadComm

LINQ to SQL語句(1)之Where

.sh ipp 大於 作用 cts eight erl div 集合 Where操作   適用場景:實現過濾,查詢等功能。   說明:與SQL命令中的Where作用相似,都是起到範圍限定也就是過濾作用的 ,而判斷條件就是它後面所接的子句。   Whe

linq to sql where 多條件 or and

using System.Linq.Expressions; public static class PredicateBuilder {     public static Expression<Func<T, bool>> True<T&g

Linq To Sql進階系列(六)用object的動態查詢與保存log篇

directory ont 簡單 lambda表達式 bind add dbo 所有 生成 動態的生成sql語句,根據不同的條件構造不同的where字句,是拼接sql 字符串的好處。而Linq的推出,是為了彌補編程中的 Data != Object 的問題。我們又該如何實現

LINQ TO SQL和Entity Framework 的關系 你解多少?

mode 最小 -m 發的 開發 content 內容 語言 account 1. LINQ TO SQL 和EF 特點: LINQ TO SQL和Entity Framework都是一種包含LINQ功能的ORM 也就是所謂的關系對象的映射。其中包括的有DBFrist

Linq To Entities的動態排序

ops 基於 做了 生成 con component 功能 bar foreach 換了工作有一個月了,一樣的工作、一樣的代碼、一樣的體力活仍就…… Linq To Entityes 也是不新玩意了,近半年來也一直與之打交道,但一直也沒對其深究過。今天新加的功能要對所有列支

Linq to SQL 的連表查詢(轉)

equal query 交集 數據庫 調用 數據 變量 bst log 關於數據庫的查詢中經常需要用到多表的連接查詢,這裏就簡單地展示關於linq的查詢功能。 1、單表的查詢 [csharp] view plain copy var query = from

LINQ to SQL活學活用(1):這要打破舊觀念

部分 res htm aac created 名稱 chang list utf 程序架構 如今比較經典的架構,看看以下圖片。 怎樣實現 在一個N層應用

學習筆記11 EF查詢相當於sql where in

lec sql blog contains 學習筆記 [] HERE var sel 兩種寫法 1、 int[] Ids={1,2,3} DBContainer db=new DBContainer(); var list=db.表明.where(a=>Ids.

T-SQLwhere 1=1

拼接字符串 tab 直接 class ava string SQ 字符 代碼 這段代碼應該是在程序(例如在Java或者C#)中生成的,where條件中1=1之後的條件是通過if塊動態變化的。例如(對於拼接字符串的情況): String sql="select * fr

Linq to sql之left join運用示例

creat extent ext mod .get then rom emp ase 示例一: var l= from a in cardsBll.GetCards() join b in usersBll.GetAllUsers() on a.Car

史上最全 ——LINQ to SQL語句

轉自:http://www.cnblogs.com/jara/p/3473996.html 史上最全 ——LINQ to SQL語句 LINQ to SQL語句(1)之Where 適用場景:實現過濾,查詢等功能。 說明:與SQL命令中的Where作用相似,都是起到範圍限定也就是過濾作

Linq to Sql學習總結3

儲存過程: 關係資料庫中的儲存過程在實體類中對映為具體的方法,直接將儲存過程拖動到對應的dbml設計檢視中即可,如圖: 在將儲存過程拖入dbml設計檢視中時,系統執行了如下命令: SET FMTONLY ON;--表示只獲取結果集的元資料(即相關列名 ) exec sp_Name SET

Linq to Sql學習總結4

延遲執行: Linq to sql 查詢句法在定義時並不會執行,只有在呼叫的時候才會執行(執行T_Sql查詢),每呼叫一次就會執行一次。對於需要多次呼叫的情況,可以使用ToList()方法先把結果集儲存下來。 DataLoadOptions DataLoadOptions options = ne

Linq to Sql學習總結5

修改併發異常ChangeConfictException //測試修改併發異常ChangeConfictException var query = from p in ctx.Products where p.Category