.NET 10中NHibernate使用数组Contains方法查询报错求助
解决.NET 10中NHibernate数组Contains查询报错问题
问题背景
在.NET 9中正常运行的NHibernate Linq查询,使用数组的Contains方法过滤Staff实体时,升级到.NET 10后抛出以下错误:
NHibernate.HibernateException: 'Evaluation failure on op_Implicit(value(System.Int64[]))'
改用List<long>类型的集合则可正常执行,但不想批量修改代码中的数组定义。
解决方案
方案1:查询内临时转换数组为IEnumerable
无需修改数组的定义,只需在Contains调用前通过AsEnumerable()将数组转为IEnumerable<long>,NHibernate可以正常解析Enumerable.Contains方法:
var ids1 = new long[] { 1, 2, 3 }; var result1 = session.Query<Staff>().Where(x => ids1.AsEnumerable().Contains(x.Id)).ToList();
方案2:添加全局扩展方法简化调用
如果项目中有多处类似场景,可编写一个扩展方法避免重复编写AsEnumerable():
public static class ArrayQueryExtensions { public static IEnumerable<T> ForNHibernateQuery<T>(this T[] array) { return array.AsEnumerable(); } }
使用示例:
var result1 = session.Query<Staff>().Where(x => ids1.ForNHibernateQuery().Contains(x.Id)).ToList();
方案3:全局配置NHibernate支持数组Contains(进阶)
如果希望从框架层面彻底解决,无需修改任何业务查询代码,可以自定义NHibernate的Linq表达式解析逻辑,注册数组类型的Contains方法支持:
- 创建自定义的HQL生成器,处理数组的
Contains方法:
using NHibernate.Linq.Functions; using System.Linq.Expressions; using System.Reflection; public class ArrayContainsHqlGenerator : BaseHqlGeneratorForMethod { public ArrayContainsHqlGenerator() { // 注册要处理的数组Contains方法 var containsMethod = typeof(long[]) .GetMethod(nameof(Array.Contains), new[] { typeof(long) })! .GetGenericMethodDefinition(); SupportedMethods = new[] { containsMethod }; } public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) { // 将数组转为IEnumerable,复用NHibernate已有的Contains解析逻辑 var enumerableExpr = Expression.Call( typeof(Enumerable), nameof(Enumerable.AsEnumerable), new[] { method.DeclaringType!.GetElementType()! }, targetObject); var enumerableContainsMethod = typeof(Enumerable) .GetMethod(nameof(Enumerable.Contains), new[] { typeof(IEnumerable<>).MakeGenericType(method.DeclaringType.GetElementType()!), method.DeclaringType.GetElementType()! })!; return visitor.Visit(Expression.Call(enumerableContainsMethod, enumerableExpr, arguments[0])).AsExpression(); } }
- 注册自定义生成器到NHibernate配置:
using NHibernate.Cfg; using NHibernate.Linq.Functions; public class CustomLinqToHqlRegistry : DefaultLinqToHqlGeneratorsRegistry { public CustomLinqToHqlRegistry() { Merge(new ArrayContainsHqlGenerator()); } } // 在NHibernate初始化时配置 var config = new Configuration(); config.LinqToHqlGeneratorsRegistry<CustomLinqToHqlRegistry>();
配置完成后,原有的数组Contains查询无需任何修改即可在.NET 10中正常运行。
内容的提问来源于stack exchange,提问作者Craig
相关产品推荐
相关产品推荐

