• 关于Linq查询条件可能为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。

    当然这些都还只是对实践结果的一种猜测,也是笔者在博客园上的处女作,欢迎园子里的前辈指点,也欢迎拍砖,谢谢大家支持。(*^__^*) 嘻嘻

  • 相关阅读:
    vue-cli3中热更新失效,修改完代码之后需要手动刷新页面才能看到改变,解决办法
    数组中的数据项包含逗号则需在首尾拼接中括号[]来区分每一项,最后数组转为字符串,以及数据恢复
    组件之间的拖拽
    工作心得
    Vue重点知识
    vue-router路由
    利用注解和反射,将Bean枚举字段的值填入相应的字段中,并转化为fastjson返回前台
    db2 获取自增主键的方法
    mybatis注解@selectKey对于db2数据库的使用
    @InsertProvider 根据bean属性,自动生成插入sql语句
  • 原文地址:https://www.cnblogs.com/liuxinqi/p/Linq.html
Copyright © 2020-2023  润新知