群里一哥們有這樣一個需求,有一張表結構如下:
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