關於Linq查詢where條件可能為null時的詭異事件


群里一哥們有這樣一個需求,有一張表結構如下:

  MenuId varchar(50) Unchecked
  MenuName varchar(50) Checked
  PatentMenuId varchar(50) Checked

測試數據如下:

 1 insert into MenuInfo values('100','父節點1',NULL)
 2 insert into MenuInfo values('101','子節點11','100')
 3 insert into MenuInfo values('102','子節點12','100')
 4 insert into MenuInfo values('103','子節點13','100')
 5 insert into MenuInfo values('104','父節點2',NULL)
 6 insert into MenuInfo values('105','子節點21','104')

這個表就不詳細介紹了,相信大家都知道。

他的具體需求是根據一個字符串(strKey)作為條件查詢,當strKey為null時,則只查出1和5這兩條數據,如果strKey不為null時,則查出所有ParentMenuId為strKey的數據。

他原來的代碼如下:

string strKey=null;

List<MenuInfo> menuList=new List<MenuInfo>();

if(strKey==null)

{

  menuList= (from menu in db.MenuInfo

         where menu.ParentMenuId==null

         select menu).ToList();

}

else

{

  menuList=(from menu in db.MenuInfo

        where menu.ParentMenuId==strKey

        select menu).ToList();

}

這樣寫肯定能實現前面說到的需求,現在就是如何去簡化這段代碼,即干掉if判斷語句。

經過長時間激烈討論,終於有人提出用三目表達式,代碼如下:

string strKey=null;

List<MenuInfo> menuList=new List<MenuInfo>();

menuList=(from menu in db.MenuInfo

      where strKey==null?(menu.ParentMenuId==null: menu.ParentMenuId==strKey)

      select menu).ToList();

但是馬上有人將其簡化成以下代碼:

string strKey=null;

List<MenuInfo> menuList=new List<MenuInfo>();

menuList=(from menu in db.MenuInfo

      where  menu.ParentMenuId==(strKey==null?null:strKey)

      select menu).ToList();

到這里貌似沒有問題了,討論到此結束。

但是這哥們經過測試,最終還是發現了問題。當strKey為null時,卻查不到任何數據。。。這是怎么回事呢?

后來這哥們把解析后的sql語句發出來一看,三目表達式確實有問題

當where 條件為 where menu.ParentMenuId==null 時,解析的sql條件為  WHERE [t0].[PatentMenuId] IS NULL

而當where條件為 where menu.ParentMenuId==strKey(此時strKey為null)時,解析的sql條件為 WHERE [t0].[PatentMenuId] = @p0

這又是為何?這時另一哥們發話了,原因是:為null的變量和null在linq語句中意義不同。

真是一語驚醒夢中人呀,很有可能是linq在解析的時候對null值進行了判斷,這樣就很好解釋了。前面簡化胡的三木運算表達式(strKey==null?null:strKey)返回的不一定是null值,所以linq解析的時候把它當成一個變量來處理了,沒有判斷其結果是不是為null,所以就直接解析成 WHERE [t0].[PatentMenuId] = @p0了。

可是筆者還是不甘心,回來查資料,又嘗試了equals方法,代碼如下:

string strKey=null;

List<MenuInfo> menuList=new List<MenuInfo>();

menuList=(from menu in db.MenuInfo

      where  menu.PatentMenuId.Equals(strKey)

      select menu).ToList();

這樣的結果跟前面用==的結果還是一樣的,即:

當where 條件為 where menu.ParentMenuId.Equals(null) 時,解析的sql條件為  WHERE [t0].[PatentMenuId] IS NULL

而當where條件為 where menu.ParentMenuId.Equals(strKey)(此時strKey為null)時,解析的sql條件為 WHERE [t0].[PatentMenuId] = @p0

就這樣胡亂嘗試一通后又發現,由於menu.ParentMenuId是String類型,而String對象對Equals方法進行了重寫,於是又一次嘗試直接用object對象的Equals方法,簡化成以下代碼:

string strKey=null;

List<MenuInfo> menuList=new List<MenuInfo>();

menuList=(from menu in db.MenuInfo

      where Equals(menu.ParentMenuId,strKey)

      select menu).ToList();

終於搞定。但是這是為什么呢?

可能是string重寫了equals,linq本身就對sting重寫的equals方法制定了等同於“==”運算符的解析機制,而沒有對object對象的equals方法制定機制。

用筆者自己的話來說,就是當用linq對象去主動比較其他對象的時候,會解析成linq對象=其他對象(@p0),結合前面那個哥們說的“為null的變量和null在linq語句中意義不同。”這個@p0雖然是C#變量值為null,但是與sql中的null不一樣,所以在sql中這個where條件不成立,自然查不出數據。而用C#中object對象的Equals方法則不會解析成linq對象=其他對象@p0


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM