是否可以在 NHibnernate IQueryable 中按 class 类型排序
Is it possible to order by class type in NHibnernate IQueryable
可以使用特殊关键字 class 在 HQL 和 ICriteria 查询中输入 class 进行排序。
criteria.AddOrder("s.class", true);
这个关键字可以用在 IQueryable OrderBy 中吗?这似乎只除了 属性 表达式和 .GetType() 不受支持。
根据您的需要,基于与特定类型比较的排序表达式应该可以工作。例如(原谅VB.NET):
Dim o = Session.Query(Of Animal).OrderBy(Function(x) If(TypeOf x Is Cat, 1, 2)).ToList()
Quick&Dirty 建议
肮脏的 hack 可能会奏效。根据我的经验,linq2NH 确实将可查询对象转换为 HQL
而无需太多检查(请参阅 其 'trick' 示例代码中的示例案例)。因此,您可以在基础实体上添加一个虚拟且未映射 public virtual string @class { get; set; }
属性,然后按它排序。
session.Query<YourBaseEntityType>().OrderBy(e => e.@class);
对其进行测试,它很可能会被翻译 'bluntly' 为有效的 HQL
查询。当然,它很丑,可能会在任何 NH 版本升级时崩溃,...
(@
是必需的,因为 class
是 C# 中的保留字。前缀 @
告诉编译器它只是一个名称,而不是 class
关键字。)
更清洁的建议
更简洁的方法是 extend linq2NH for supporting a Class()
method on your entities. (Blog post found on this answer to another question. Edit: here is another interesting implementation example on SO, and here is a list of links which provides much more information on linq-to-nhibernate 可扩展性。)
根据您的情况调整上面 'extend' 博客 post 的链接,它应该类似于以下步骤。
首先,您需要一个可在您的实体上使用的扩展方法。类似于:
public static class YourLinqExtensions
{
public static string Class(this YourEntityBaseClass source)
{
// .Net runtime implementation just in case, useless in linq2nhib case
return source == null ? null : source.GetType().Name;
}
}
然后,实现其 HQL
翻译:
public class ClassGenerator : NHibernate.Linq.Functions.BaseHqlGeneratorForMethod
{
public ClassGenerator()
{
SupportedMethods = new[]
{
NHibernate.Linq.ReflectionHelper.GetMethod(
() => YourLinqExtensions.Class(null))
};
}
public override NHibernate.Hql.Ast.HqlTreeNode BuildHql(MethodInfo method,
Expression targetObject,
ReadOnlyCollection<Expression> arguments,
NHibernate.Hql.Ast.HqlTreeBuilder treeBuilder,
NHibernate.Linq.Visitors.IHqlExpressionVisitor visitor)
{
return treeBuilder.Dot(
// using NHibernate.Hql.Ast; required for .AsExpression() to be found
// at compile time.
visitor.Visit(arguments[0]).AsExpression(),
// Below 'Class' method is not 'YourLinqExtensions.Class' extension
// method. It is natively available on HqlTreeBuilder.
treeBuilder.Class());
}
}
使用生成器扩展默认的 linq2NH 注册表:
public class YourLinqToHqlGeneratorsRegistry:
NHibernate.Linq.Functions.DefaultLinqToHqlGeneratorsRegistry
{
public YourLinqToHqlGeneratorsRegistry()
{
// using NHibernate.Linq.Functions; required for .Merge() to be found
// at compile time.
this.Merge(new ClassGenerator());
}
}
并配置 NH 以使用您的新注册表。使用 hibernate.cfg.xml,应该类似于在 session-factory
节点下添加以下 property
节点:
<property name="linqtohql.generatorsregistry">YourNameSpace.YourLinqToHqlGeneratorsRegistry, YourAssemblyName</property>
用法:
session.Query<YourBaseEntityType>().OrderBy(e => e.Class());
(按照我详细说明的方式,它至少应该可以编译,但它完全没有经过测试。编辑:现在已经经过测试。)
可以使用特殊关键字 class 在 HQL 和 ICriteria 查询中输入 class 进行排序。
criteria.AddOrder("s.class", true);
这个关键字可以用在 IQueryable OrderBy 中吗?这似乎只除了 属性 表达式和 .GetType() 不受支持。
根据您的需要,基于与特定类型比较的排序表达式应该可以工作。例如(原谅VB.NET):
Dim o = Session.Query(Of Animal).OrderBy(Function(x) If(TypeOf x Is Cat, 1, 2)).ToList()
Quick&Dirty 建议
肮脏的 hack 可能会奏效。根据我的经验,linq2NH 确实将可查询对象转换为 HQL
而无需太多检查(请参阅 public virtual string @class { get; set; }
属性,然后按它排序。
session.Query<YourBaseEntityType>().OrderBy(e => e.@class);
对其进行测试,它很可能会被翻译 'bluntly' 为有效的 HQL
查询。当然,它很丑,可能会在任何 NH 版本升级时崩溃,...
(@
是必需的,因为 class
是 C# 中的保留字。前缀 @
告诉编译器它只是一个名称,而不是 class
关键字。)
更清洁的建议
更简洁的方法是 extend linq2NH for supporting a Class()
method on your entities. (Blog post found on this answer to another question. Edit: here is another interesting implementation example on SO, and here is a list of links which provides much more information on linq-to-nhibernate 可扩展性。)
根据您的情况调整上面 'extend' 博客 post 的链接,它应该类似于以下步骤。
首先,您需要一个可在您的实体上使用的扩展方法。类似于:
public static class YourLinqExtensions
{
public static string Class(this YourEntityBaseClass source)
{
// .Net runtime implementation just in case, useless in linq2nhib case
return source == null ? null : source.GetType().Name;
}
}
然后,实现其 HQL
翻译:
public class ClassGenerator : NHibernate.Linq.Functions.BaseHqlGeneratorForMethod
{
public ClassGenerator()
{
SupportedMethods = new[]
{
NHibernate.Linq.ReflectionHelper.GetMethod(
() => YourLinqExtensions.Class(null))
};
}
public override NHibernate.Hql.Ast.HqlTreeNode BuildHql(MethodInfo method,
Expression targetObject,
ReadOnlyCollection<Expression> arguments,
NHibernate.Hql.Ast.HqlTreeBuilder treeBuilder,
NHibernate.Linq.Visitors.IHqlExpressionVisitor visitor)
{
return treeBuilder.Dot(
// using NHibernate.Hql.Ast; required for .AsExpression() to be found
// at compile time.
visitor.Visit(arguments[0]).AsExpression(),
// Below 'Class' method is not 'YourLinqExtensions.Class' extension
// method. It is natively available on HqlTreeBuilder.
treeBuilder.Class());
}
}
使用生成器扩展默认的 linq2NH 注册表:
public class YourLinqToHqlGeneratorsRegistry:
NHibernate.Linq.Functions.DefaultLinqToHqlGeneratorsRegistry
{
public YourLinqToHqlGeneratorsRegistry()
{
// using NHibernate.Linq.Functions; required for .Merge() to be found
// at compile time.
this.Merge(new ClassGenerator());
}
}
并配置 NH 以使用您的新注册表。使用 hibernate.cfg.xml,应该类似于在 session-factory
节点下添加以下 property
节点:
<property name="linqtohql.generatorsregistry">YourNameSpace.YourLinqToHqlGeneratorsRegistry, YourAssemblyName</property>
用法:
session.Query<YourBaseEntityType>().OrderBy(e => e.Class());
(按照我详细说明的方式,它至少应该可以编译,但它完全没有经过测试。编辑:现在已经经过测试。)