任意长度ORDER by使用NHibernate

本文关键字:使用 NHibernate by ORDER 任意长 | 更新日期: 2023-09-27 18:13:25

如果我使用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查询。其中最有希望的是nHibernate中的Dynamic QueryOver,但我很难完全确定这是否在正确的方向上。

任意长度ORDER by使用NHibernate

那么,问题是你在这里没有执行任何东西,所以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>>

,这可能不起作用。

我对这个问题很感兴趣,想试着让@DarrenKopp的回答变得通用。我的代码比我预期的要冗长,但我相信它确实有效。我测试了Linq to Objects,所以nHibernate的Linq提供程序是未测试的。

代码在这里。

你可以像这样调用它…

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

下面是关于动态排序条件的一个快速概念证明。你可能会发现,避免通过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;
    }
}