使用NHibernate的任意长度ORDER BY

时间:2014-07-16 23:10:23

标签: c# nhibernate

如果我使用PHP和MySQL进行此操作,它看起来就像这样(免责声明此PHP代码适合外部/面向Web使用,因为它易受攻击到SQL注入):

<?php

function orderByColumns ($columns, $sql) {
    if (0 < count($columns)) {
        $column = array_shift($columns);
        if (! stripos($sql, "ORDER BY")) {
            $sql .= " ORDER BY";
        }
        $sql .= " {$column['name']} {$column['dir']}";
        $sql .= 0 < count($columns) ? "," : ""; 
        return orderByColumns($columns, $sql);
    }
    return $sql;
}

$columns = array(
    array(
        "name" => "foo",
        "dir" => "ASC"
    ),
    array(
        "name" => "bar",
        "dir" => "DESC"
    )
);

$sql = "SELECT * FROM baz";

$sql = orderByColumns($columns, $sql); // And from here I could make my query

关键是$columns是来自某个用户的输入,并且可用于在不事先知道列表的情况下对列进行排序,以及可重用的方法。

我正在寻找一种使用C#,特别是NHibernate做类似事情的方法,但它似乎并没有真正起作用。以下是我在C#中尝试过的内容:

List<string> columns = new List<string>()
{
    "Column1",
    "Column2",
    "Column3"
    // And there could be more.
}
string column = columns.First();

fq = foo.Queryable.OrderBy(
    i => i.GetType().GetProperty(column).GetValue(i, null)
);

foreach (string column in columns)
{
    fq = fq.ThenBy(
        i => i.GetType().GetProperty(column).GetValue(i, null)
    );    
}

而且,我已经查看了一些StackOverflow的答案(好吧,不止一些),但他们似乎并没有像我一样动态地解决如何动态构建NHibernate查询的问题。寻找。感觉最有希望的是Dynamic QueryOver in nHibernate,但是我很难完全知道这是否是正确的方向。

3 个答案:

答案 0 :(得分:2)

所以,问题在于你现在没有执行任何操作,所以nhibernate会尝试将其转换为SQL,因为它不知道GetType()会抱怨方法

你必须建立你自己的Expression实例,并且没有很好的方法可以动态地做到这一点,虽然可以做到,但仍然没有乐趣。

我认为制作lambda表达式和列的字典会更容易

var lookup = new Dictionary<string, Expression<Func<T, object>>> {
    { "ColumnA", x => x.ColumnA },
    { "ColumnB", x => x.ColumnB }
};

foreach (string column in columns) {
    fq = fq.ThenBy(lookup[column]);
}

即便如此,如果抱怨Expression<Func<T,object>>

,这可能也行不通

答案 1 :(得分:1)

我对这个问题很感兴趣,并希望能够让@ DarrenKopp的答案变得通用。我的代码比我预期的更加冗长,但我相信它确实有效。我使用Linq对象进行测试,因此nHibernate的Linq提供程序未经测试。

代码可用here

你可以用这样的东西来打电话......

var sortedItems = items.OrderBy(
    new OrderByKeyInfo ("MyPropertyA", OrderByDirection.Descending),
    new OrderByKeyInfo ("MyPropertyB", OrderByDirection.Ascending),
    new OrderByKeyInfo ("MyPropertyC", OrderByDirection.Ascending));

答案 2 :(得分:0)

这是围绕动态排序条件的快速概念证明。您可能会发现避免通过NHibernate访问数据库可能会更好,因为如果初始排序包含例如8条记录,则可能会让用户感到困惑,但是再次排序数据会返回9,因为添加了新记录之间和现在显示为我们已经回到数据库而不是仅仅重新排序内存中的集合 - 而且我不确定它是否/如何映射到NHibernate。

对于控制台应用程序来说,这是一个快速而肮脏的解决方案,只是为了证明它能够正常工作,毫无疑问会有一些调整和优化。最终,超载

List<T>.Sort(Comparison<T>)

是防止必须动态创建实现T的IComparer的类的那个:

class Program
{
    private class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public int NumberOfChildren { get; set; }
    }

    private static List<Person> people = new List<Person>()
{
    new Person() { Name="Andrew", Age=35, NumberOfChildren=3},
    new Person() { Name="Maria",Age=33,NumberOfChildren=3},
    new Person() {Name="Tim",Age=67,NumberOfChildren=4},
    new Person() {Name="Tim",Age=62,NumberOfChildren=2},
    new Person() {Name="Jim", Age=67,NumberOfChildren=2},
    new Person() {Name="Tim",Age=33,NumberOfChildren=0},
    new Person() {Name="Bob",Age=35,NumberOfChildren =3},
    new Person() {Name="Daisy",Age=1,NumberOfChildren=0}
};

    static void Main(string[] args)
    {
        List<string> sortConditions = new List<string>() { "Age", "Name", "NumberOfChildren" };

        var properties = GetSortProperties<Person>(sortConditions);

        people.Sort((Person a, Person b) =>
        {
            int result = 0;

            foreach (PropertyInfo prop in properties)
            {
                result = ((IComparable)prop.GetValue(a, null)).CompareTo(prop.GetValue(b, null));

                if (result != 0)
                    break;
            }

            return result;
        });
    }

    static List<PropertyInfo> GetSortProperties<T>(List<string> propertyNames)
    {
        List<PropertyInfo> properties = new List<PropertyInfo>();

        var typeProperties = typeof(T).GetProperties();

        foreach (string propName in propertyNames)
        {
            properties.Add(typeProperties.SingleOrDefault(tp => tp.Name == propName));
        }

        return properties;
    }
}