3

我正在尝试构建一个通用查询机制来访问我的存储库。我希望使用 Lambda 表达式来过滤和排序查询。我正在努力寻找一种方法来传递一个通用 Lambda 表达式列表,特别是用于 order-by,并且希望能得到帮助。

编辑:我试图满足的两个要求是,不要将 IQueryable 暴露在存储库之外,但仍然能够在数据库级别进行一些过滤和排序。

为了更好地说明这一点,让我向您展示代码

public class Query<T>
{
    public class OrderBy<T>
    {
        public Expression<Func<T, **int**>> Clause { set; get; } // Order By clause
        public bool Descending = true;
    }

    public Expression<Func<T, bool>> Where { set; get; } // Where clause
    public IList<OrderBy<T>> OrderBys { set; get; } // Where clause

    public Query()
    {
        OrderBys = new List<OrderBy<T>>();
    }
}    

public IEnumerable<Person> FindBy(Query<Person> query)
{
    IQueryable<Person> Temp = GetObjectSet();
    if (query.Where != null) 
        Temp = Temp.Where(query.Where);
    foreach (var OrderByThis in query.OrderBys)
    {
        if (OrderByThis.Descending) 
            Temp = Temp.OrderByDescending(OrderByThis.Clause);
        else 
            Temp = Temp.OrderBy(OrderByThis.Clause);
    }

    return Temp.ToList<Person>();
}

这一切都很好,但是 Expression< Func< T, int >> 不是通用的。我需要能够执行以下操作:

Query<Person> query = new Query<Person>();
Query<Person>.OrderBy<Person> clause1 = new Query<Person>.OrderBy<Person>();
clause1.Clause = m => m.Username;
Query<Person>.OrderBy<Person> clause2 = new Query<Person>.OrderBy<Person>();
clause2.Clause = m => m.DateOfBirth;
query.OrderBys.Add(clause1);
query.OrderBys.Add(clause2);

即添加许多不同类型的不同字段。

我想必须有一种方法将这些存储为通用 Lambda 函数,然后在存储库中转换为它需要的强类型 Lambda 函数。

我怎样才能做到这一点?

4

2 回答 2

1

详细说明我的评论,我不明白为什么需要从 Expressions 构建自己的中间查询对象,然后从该中间对象重建 Expressions,而您可以完全跳过该翻译。

鉴于您的示例查询:

repository.FindBy(people => people.OrderBy(p => p.Username).ThenBy(p => p.DateOfBirth));

请注意,您仍然可以增量构建查询,例如,如果它是基于用户选择完成的。以下查询等效于上述查询:

Func<IEnumerable<Person>, IEnumerable<Person>> query = people => people.OrderBy(p => p.Username);
 query = query.ThenBy(p => p.DateOfBirth);

我知道您不想IQueryable在存储库之外公开,但您仍然可以使用带有签名的 LINQ,例如:

public IEnumerable<Person> FindBy(Func<IEnumerable<Person>, IEnumerable<Person>> query)
{
    return query(GetObjectSet()).ToList();
}

然而,谈到您的实际问题,您可以通过使用属性类型来完成您的任务OrderBy,或者如果这让您感到不安,您可以通过使用而不是对象来限制它,因为它实际上是您订购所需的全部,并且字符串和数字类型都实现了它。Expression<Func<T, object>>ClauseIComparable

于 2012-08-16T02:05:02.013 回答
1

正如我在回答您的其他问题时指出的那样,我一般不鼓励这种方法。IQueryable<T>仅仅暴露/更有意义IOrderedQueryable<T>

话虽如此,在如何将多个表达式传递给 EF 的 OrderBy 的选定答案中,有一个符合您意图的解决方案?.

它允许您使用如下语法:

var query = context.Users ... ;

var queryWithOrderBy = ApplyOrderBy(query,
    new OrderByExpression<User, string>(expression: u => u.UserName, descending: false),    // a string, asc
    new OrderByExpression<User, int>(expression: u => u.UserId, descending: true));  // an int, desc

var result = queryWithOrderBy.ToList(); // didn't throw an exception for me
于 2012-08-16T03:43:08.733 回答