如何使用NHibernate QueryOver查询逗号分隔字符串列不包含指定值?
解决NHibernate QueryOver查询逗号分隔字符串不包含特定值的问题
你的问题根源在于:NHibernate的QueryOver(或LINQ查询)需要把你写的C#表达式转换成数据库能执行的SQL语句,但你用的Split()、List.Contains()都是.NET客户端的内存操作,NHibernate无法将这些方法映射成对应的SQL逻辑,所以才会抛出"Unrecognised method call"的异常。
下面给你两种可行的解决方案:
方案1:直接使用SQL字符串匹配逻辑
因为你的DoNotDisplay列是逗号分隔的字符串,要判断它不包含独立的"3"(避免匹配到"13"、"31"这类包含3但不是独立值的情况),可以通过SQL的字符串匹配来实现。
通用版(兼容多数数据库)
通过构建Disjunction来覆盖所有可能包含"3"的场景,同时处理空值/空字符串的情况:
var query = session.QueryOver<Host>() // 排除所有包含独立"3"的情况 .Where(Restrictions.Not( Restrictions.Disjunction() .Add(Restrictions.Eq("systemsNotToBeShown", "3")) // 字段就是"3" .Add(Restrictions.Like("systemsNotToBeShown", "3,%")) // 3在开头 .Add(Restrictions.Like("systemsNotToBeShown", "%,3")) // 3在结尾 .Add(Restrictions.Like("systemsNotToBeShown", "%,3,%")) // 3在中间 )) // 包含空值或空字符串的情况(这些也满足不包含3的条件) .Where(Restrictions.Or( Restrictions.IsNull("systemsNotToBeShown"), Restrictions.Eq("systemsNotToBeShown", "") ));
简洁版(根据数据库调整)
可以通过给字段前后拼接逗号,统一判断是否包含,3,,这样能一次覆盖所有场景:
- 如果你用MySQL:
var query = session.QueryOver<Host>() .Where("CONCAT(',', systemsNotToBeShown, ',') NOT LIKE '%,3,%'");
- 如果你用SQL Server:
var query = session.QueryOver<Host>() .Where("',' + systemsNotToBeShown + ',' NOT LIKE '%,3,%'");
- 如果你用Oracle:
var query = session.QueryOver<Host>() .Where("',' || systemsNotToBeShown || ',' NOT LIKE '%,3,%'");
方案2:优化数据库设计(推荐)
如果这个查询是业务中的常用场景,强烈建议把逗号分隔的DoNotDisplay字段改成关联表:
- 创建一个
HostSystemExclusion表,包含HostId和SystemId两个字段 - 在
Host类中添加IList<HostSystemExclusion> ExcludedSystems的导航属性 - 之后查询就可以用标准的关联查询,性能更优,也符合关系型数据库的设计规范:
var query = session.QueryOver<Host>() .WhereNotExists( QueryOver.Of<HostSystemExclusion>() .Where(e => e.Host.Id == Host.Id) .And(e => e.SystemId == 3) );
为什么你之前的尝试失败?
- 第一种方式的
h.systemsNotToBeShown.Split(',').Contains("3"):Split()是.NET在内存中拆分字符串的方法,NHibernate无法把这个操作转换成SQL的字符串处理逻辑 - 第二种方式的
h.SystemsNotToBeShown.Contains(3):这个Contains()是List<T>的内存方法,同样无法被NHibernate映射成SQL的查询条件
内容的提问来源于stack exchange,提问作者ma268
相关产品推荐
相关产品推荐

