2012-08-16 67 views
2

我想构建一个通用的查询机制来访问我的存储库。我希望使用Lambda表达式来筛选和排序查询。我正在努力寻找一种方法来传递通用的Lambda表达式列表,特别是为了顺序排列,并希望能够为此提供帮助。编辑:2要求我试图满足的是,不暴露超出存储库的IQueryable,但仍然能够在数据库级别进行一些过滤和排序。OrderBy Lambda的泛型列表

为了更好地说明这一点让我告诉你的代码

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>(); 
    } 

这是非常好的,但表达< 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函数。

我该怎么做?

+0

.NET已经提供订购和排序,以扩展方法,如'.OrderBy'和'机制。在哪里和一个更干净的语法。为什么不公开像'repository.Get(Func ,IEnumerable queryExpression)''? (或者如果你需要解析表达式树,使它成为'Expression Jay 2012-08-16 01:31:12

+0

我不太喜欢对不起,可以举一个具体的例子吗? – 2012-08-16 01:48:14

+0

你可以使用'object'作为你的类型吗? – Gabe 2012-08-16 01:49:26

回答

1

正如我在我对your other question的回答中所指出的那样,我一般会劝阻这种方法。暴露IQueryable<T>/IOrderedQueryable<T>更有意义。

这就是说,在您选择的How to pass multiple Expressions to OrderBy for EF?答案中可以找到符合您意向的解决方案。

它允许您使用的语法,如:

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 
+0

谢谢@smartcaveman,我很高兴地得出同样的结论。我仍然希望得到这个通用查询排序,但我怀疑我最终只会暴露IQueryable。 – 2012-08-16 04:02:09

+1

@DaleBurrell,你会的。在走向我的感官之前,我也走了一段路。 – smartcaveman 2012-08-16 04:08:03

+0

那么解决方案的工作原理,我确信有一个更好的解决方案。但无论如何回到IQueryable :)谢谢 – 2012-08-16 05:09:08

1

详细阐述了我的评论,我不明白为什么需要从表达式中构建自己的中间查询对象,然后重构该中间对象中的表达式,当时可以完全跳过该翻译。

鉴于您的示例查询:

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(); 
} 

但是,对于您的实际问题,您可以使用Expression<Func<T, object>>Clause属性类型实现您的OrderBy任务,或者如果这使您感到不安,则可以使用IComparable而不是对象,因为它实际上就是你需要的所有命令,并且字符串和数字类型都可以实现它。

+0

来解决排序问题我刚试过你的解决方案,并且意识到我已经忘记提到2个要求,即不要将IQueryable暴露在存储库之外,但仍然能够执行一些数据库级别的过滤和排序。您的解决方案不使用实体框架,它假定是因为它使用IEnumerable吗? – 2012-08-16 02:26:39

+0

@DaleBurrell好的,我在问题中添加了EF标签。您是否尝试过第二种选择('FindBy(Func Jay 2012-08-16 02:31:22

+0

感谢Jay,是的,我正在使用第二个选项,并且在查询分析器中它没有传递任何过滤/排序信息。使用IComarable很好地编译!但代码本身不起作用 - 这可能是另一个问题的主题,除非你有任何想法? – 2012-08-16 02:48:49