Dynamic LINQ on a collection?

Asked
Viewd3487

5

I've a project which ask me to do such a BIG search engine but which is all dynamic. I mean I can have about 0 to 9 main "group" which have inside something like an infinite possibility of "where" with "OR" or "AND". First thing we think was to use Dynamic Linq which provide a good alternative to build dynamic query. All this using EF with an homemade wrapper.

Probleme : I'm not able to access to a "Collection". I mean, I can easly access to a referenced object (Like Customer.State.StateName = "New-York" OR Custoemr.State.StateName = "Quebec" ) but I can't find a way to acces to something like : "Customer.Orders.OrderID = 2 OR Customer.Orders.OrderID = 3". I can easly figure out this its because its a collection, but how can I do this?

Please help me out!!

** Sorry for my english !!


Update

I'm not clear enought I think, sorry its because im french...

My problem its because nothing is static. Its a candidat search engine for a recruting compagny that place candidats into an enterprise. In a page where manager can search candidat, he can "parse" by : Domain(s) (Jobs), City(ies) or many other that user have filled up when he register. All this in format (if it were in SQL) :

[...] WHERE (domaine.domainID = 3 OR domaine.domainID = 5 OR domaine.domainID = 23) AND (cities.cityID = 4, cities.city = 32) [...]

So i can't do this with a normal LINQ format like :

Candidat.Domaines.Where(domain => domain.DomainID == 3 || domain.DomainID == 5 || domain.DomainID == 23);

Even the operator in the paretheses are dynamic ("AND" or "OR")! That why we trying to use Dynamic Linq because its a lot more flexible.

Hope its more easy to understand my problem ...


Update 2 Here's my method

private string BuildDomainsWhereClause() {
        StringBuilder theWhere = new StringBuilder();

        if (this.Domaines.NumberOfIDs > 0) {
            theWhere.Append("( ");

            theWhere.Append(string.Format("Domaines.Where( "));
            foreach (int i in this.Domaines.ListOfIDs) {
                if (this.Domaines.ListOfIDs.IndexOf(i) > 0) {
                    theWhere.Append(string.Format(" {0} ", this.DispoJours.AndOr == AndOrEnum.And ? "&&" : "||"));
                }
                theWhere.Append(string.Format("DomaineId == {0}", i));
            }
            theWhere.Append(" ))");
        }

        return theWhere.ToString();
    }

It works great instead that it "Not return a boolean". So how should I? Error : "Expression of type 'Boolean' expected".

At the end, it returns something like : "( Domaines.Where( DomaineId == 2 && DomaineId == 3 && DomaineId == 4 && DomaineId == 5 ))." which is added to my LINQ Query :

var queryWithWhere = from c in m_context.Candidats.Where(WHERE)
                                     select c;

Dont forget that there's like 7 or 8 more "possible" added things to search in ... Any ideas?

5 个答案

5

您在这里需要做的是建立一个 LambdaExpression (更具体地说是Expression<Func<T, bool>>)。您不能使用字符串。您可以构建一个像这样的简单表达式:

 ParameterExpression p = Expression.Parameter(typeof(Domaine), "domaine");
Expression<Func<Domaine, bool>> wherePredicate = 
  Expression.Lambda<Func<Domaine, bool>>(
    Expression.Or(
      Expression.Equal(
        Expression.Property(p, "DomainID"),
        Expression.Constant(10)),
      Expression.Equal(
        Expression.Property(p, "DomainID"),
        Expression.Constant(11))
      ), p);
 

domaine.DomainID = 10 || domaine.DomainID = 11

如果您需要手动操作,则可读性不强。

有一个完全可操作的表达式解析器的示例,它将根据 DynamicQuery 下的em> Visual Studio 2008 C#示例 。 (LinqDataSource控件在内部使用了此示例的稍作修改的版本。)

  • Thanks a lot. Its not exactly what i’ve expect, but it looks great. I’ve still got to tricks it up a little bit to make it perfectly dynamic. But thanks, very usefull.

    Simon DugréAugust 20, 2009 14:58
1

假设Customer.Orders返回一个集合,这正是您不能仅调用其属性的原因。

为了使用LINQ来获取您要查找的订单,您需要知道OrderID(或其他属性),在这种情况下,您可以执行以下操作:

 Customer.Orders.Find(order => order.OrderID == 2);
 

编辑:以这种方式添加表达式以查找ID 2或3:

 Customer.Orders.FindAll(order => order.OrderID == 2 || order.OrderID == 3);
 
4

最后,我完全按照我想要的方式来做。

 private string BuildDomainsWhereClause() {
        StringBuilder theWhere = new StringBuilder();

        if (this.Domains.NumberOfIDs > 0) {
            theWhere.Append("( ");

            foreach (int i in this.Domains.ListOfIDs) {
                if (this.Domains.ListOfIDs.IndexOf(i) > 0) {
                    theWhere.Append(string.Format(" {0} ", this.Domains.AndOr == AndOrEnum.And ? "&&" : "||"));
                }
                theWhere.Append(string.Format("Domains.Any(IdDomaine== {0})", i));
            }
            theWhere.Append(" )");
        }

        return theWhere.ToString();
    }
 

产生如下内容:“((DispoJours.Any(IdDispo == 3)&& DispoJours.Any(IdDispo == 5))”。

我所有其他的“在哪里构建器”都将执行相同的操作,并在其中加上“ &&”,以给出正确的结果。

以后:

 var queryWithWhere = from c in m_context.Candidats.Where(WHERE)
                     select c;
 

哇!谢谢大家。非常有用!喜欢这个网站!


更新

别忘了我在此查询上使用Dynamic Linq。这不是普通的LINQ查询。

  • If you’re going to be building actual queries with strings, make sure to parametrize the strings, or you could be susceptible to SQL injection attacks. Although it might be the easier way, sometimes it’s not necessarily the best/safest way. If you use LINQ for your queries, it automatically parametrizes queries for you.

    MunkiPhDAugust 20, 2009 17:23
  • Thanks a lot to prevents me. Now that we’re not using anymore SQL, I’ve forget that dangerous things. Thanks. But in my case, user do not write “1”, he selects it in a checkboxlist so the danger isn’t realy there. I will do it anyway, but were not realy a danger.

    Simon DugréAugust 20, 2009 17:39
0

如果您退后一步,问客户想要做什么。

 Filter bug information.
 

为什么不将数据导出到excel或将excel指向SQL表。构建起来并不是那么有趣,但是您将在几个小时内完成工作,而不是几天或几周。 :)

  • Yea i know!! But we thought that Dynamic LINQ were doin’ exactly what we were searching for …

    Simon DugréAugust 20, 2009 13:30
1

我是否理解这样的权利:“客户”都是集合,“订单”都是集合,而“州”(显然)是“财产”?

 var q = from a in Customer
    from b in a.Orders
    where b.ID == 2
              || b.ID == 3
    select b;
 

我想可以的。

修改

我做了类似的事情。确切确定我的做法已经太久了,但是我可以告诉你,我正在使用

 public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values);
 

来自DynamicQueryable类。

 this.CountrySitesObject.Sites.AsQueryable().Where(w.WhereQuery, w.WhereParameters) 
 

(从我的代码中复制)。

  • Yes it works great. But not when you don’t realy know how many “b.Id” you have to search for. Even not if its a “   ” or “&&” between.
    Simon DugréAugust 20, 2009 13:33
  • I will try this too. But i’m on a solution which seems to works. I’ll come back later to give you news.

    Simon DugréAugust 20, 2009 15:25