是否可以按照NHibnernate IQueryable中的类类型进行排序

时间:2016-02-18 17:26:18

标签: c# nhibernate iqueryable linq-to-nhibernate

可以使用特殊关键字类在HQL和ICriteria查询中按类类型进行排序。

criteria.AddOrder("s.class", true);

这个关键字可以用在IQueryable OrderBy中吗?这似乎只是在表达式和.GetType()不受支持之外。

2 个答案:

答案 0 :(得分:4)

快速和肮脏的建议

肮脏的黑客可能会起作用。根据我的经验,linq2NH确实可以将查询转换为HQL,而无需进行太多检查(请参阅this question查看其“诀窍”示例代码中的示例案例)。因此,您可以在基础实体上添加一个虚拟且未映射的public virtual string @class { get; set; }属性,然后按顺序排序。

session.Query<YourBaseEntityType>().OrderBy(e => e.@class);

测试它,它很可能会直接翻译成#&到一个有效的HQL查询。当然,它很难看,可能会在任何NH版本升级时破坏,......

@是必需的,因为class是C#中的保留字。使用@的前缀告诉编译器它只是一个名称,而不是class关键字。 )

清洁建议

更简洁的方法是extend linq2NH支持实体上的Class()方法。 (在this answer to another question上找到博客帖子。编辑:这是另一个有趣的implementation example on SO,这里是list of links,它提供了有关可扩展性的更多信息。)

适应以上链接&#39;延伸&#39;博客文章发布到你的案例,它应该是类似的步骤。

首先,您需要一个可在您的实体上使用的扩展方法。类似的东西:

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,它应该像在property节点下添加session-factory节点一样:

<property name="linqtohql.generatorsregistry">YourNameSpace.YourLinqToHqlGeneratorsRegistry, YourAssemblyName</property>

用法:

session.Query<YourBaseEntityType>().OrderBy(e => e.Class());

(通过我详细阐述所有这些的方式,它至少应该编译,但它完全未经测试。编辑:现在它已经过测试。)

答案 1 :(得分:1)

根据您的需要,基于与特定类型的比较的排序表达式应该有效。例如(原谅VB.NET):

Dim o = Session.Query(Of Animal).OrderBy(Function(x) If(TypeOf x Is Cat, 1, 2)).ToList()