none
关于SilverLight 中LINQ动态查询问题 RRS feed

  • 问题

  • 我想实现如下SQL代码:select * from database where Name in ("Lu","tom")

    用LINQ如下:var q=from c in ... where (new string[]{"Lu","tom"}).Contains(c.Name) select c;

    这句话总报错,好像是说不支持contains方法。

    这句话里 ("Lu","tom")是动态添加的。不知道还有什么更好的方法。恳请赐教!

    2011年2月26日 19:08

答案

  • 找到问题了。应该是粗心大意,随意照抄导致的后果。

    为了偷懒,写了个var,我的是RIA读取数据库中的内容,我那句话其实是定义了一个查询语句,所以它产生的类型是:EntityQuery<G_WorkPlan> ,其实编译器想说的是EntityQuery<G_WorkPlan> 不支持contains吧?

    后面又转换了一下,用的这个IEnumerable<G_WorkPlan>。

    下面是源码。

    EntityQuery<G_WorkPlan>  query=from a from myPlanService.GetG_WorkPlanQuery()   select a;

    myPlanService.load(query);//这里把数据读出来。

    在myPlanService_PropertyChanged中:

    if(e.PropertyName == "IsLoading")

    {

          IEnumerable<G_WorkPlan> a=from b in myPlanService.G_WorkPlans   where  lstStrings.Contains(b.类型.Trim())    select b;

     

    }

    2011年2月27日 7:17

全部回复

  • 朋友你的语句的确写错了,微软的编译器没有骗你,数组的确没有提供Contains方法,除非你给数组加上一个Contains方法,但是IEnumrable类型的确提供了Contains,因此你可用from X in ...这个Linq语句将你的条件数据(数组)转化为IEnumrable类型,这样就可以使用Contains方法。

    呵呵,事实上,因为我是老程序员,总是用喜欢老方法,接触Linq也不久,也不比你好,下面就举个正确的例子供你参考:

            public void SearchTest()
            {
                var Data = new List<CDataItem>();
                Data.Add(new CDataItem() { Name = "Lu", AddMsg = "Lu is clever.", });
                Data.Add(new CDataItem() { Name = "TFSoft", AddMsg = "TFSoft is stupid.", });
                Data.Add(new CDataItem() { Name = "Tom", AddMsg = "Tom is smart.", });

                var Lnq = from X1 in Data
                          where (from X2 in (new string[] { "Lu", "Tom" }) select X2).Contains(X1.Name)
                          select X1;

                MessageBox.Show(Lnq.Count().ToString()); // 2 records foud.
            }

            public class CDataItem
            {
                public string Name { get; set; }
                public string AddMsg { get; set; }
            }

     

    2011年2月26日 20:16
  • 首先,非常感谢TFSoft这么快给我回复。

    但,您的方法我试了一下,并没有通过。还是一样的错误。不知是什么原因。

    以下是关于这个问题我在网上找到的方法,但我不是很理解。现贴出来。

    方法一:

    在Linq动态条件中提供一些方法允许我们动态构造Lambda表达式。如Expression.Call, Expression.Or, Expression.And,这样代码就可以写成:

    1. ParameterExpression c = Expression.Parameter(typeof(Customer), "c");  
    2.  Expression condition = Expression.Constant(false);  
    3.  foreach (string s in starts)  
    4.  {  
    5.      Expression con = Expression.Call(  
    6.          Expression.Property(c, typeof(Customer).GetProperty("CustomerID")),  
    7.          typeof(string).GetMethod("StartsWith"
    8. new Type[] { typeof(string) }),  
    9.          Expression.Constant(s));  
    10.      condition = Expression.Or(con, condition);  
    11.  }  
    12.  Expressionbool>> end =  
    13.      Expression.Lambdabool>>
    14. (condition, new ParameterExpression[] { c });  

    现在来解释Linq动态条件这段代码,首先构造了一个ParameterExpression对象,它作为参数传到Lambda表达中(相当于c => c.CustomerID.StartsWith("A")这里的c)。然后用值为false的Expression用来初始化该表达式(Expression.Constant(false))。

    1. Expression con = Expression.Call(  
    2.      Expression.Property(c, typeof(Customer).GetProperty("CustomerID")),  
    3.      typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }),  
    4.      Expression.Constant(s));  
    5.  condition = Expression.Or(con, condition);  

    上面这段代码是重头戏,用Expression.Call方法动态构造一个表达式,其中Expression.Property(c, typeof(Customer).GetProperty("CustomerID"))转换为c.CustomerID,typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) })表示string的StartsWith(string)方法,Expression.Constant(s)表示字符串常量,这个方法可以表示成:c.CustomerID.StartsWith(s)。然后调用Expression.Or方法组合各个条件(根据逻辑关系不同调用不同的方法,如or,and...)。

    最后构造成Lambda表达式,现在就可以使用它了 ctx.Customers.Where(end)。

     

    方法二:

    限定字段在某集合中
    这有点像in操作。比如where city in ('London', 'BeiJing') 也可以写成 where city = 'London' or city = 'BeiJing'。既然谈到or条件的动态构造了,那就也来构造下这个吧。看上去有点多此一举。但是,至少是个很好的学习机会。这个和上面不同的是,它条件字段是唯一的,变化的是该字段的值。那用一string将字段名成传入,并用一集合将字段值传入函数。
    该函数完整的定义入下:

     


        
    public static IQueryable<TEntity> WhereOr<TEntity, OrType>(this IQueryable<TEntity> source,
    string
     propertyName, IEnumerable<OrType> values)
        
    {
            
    if (source == null)
                
    throw new ArgumentNullException("Source can't be null!!");
            ParameterExpression param 
    = Expression.Parameter(typeof(TEntity), "p");
            Expression left 
    = Expression.Property(param, propertyName);
            Expression condition 
    = null;
            
    foreach (OrType value in values)
            
    {
                Expression filter 
    = Expression.Equal(left, Expression.Constant(value));
                
    if (condition == null)
                    condition 
    = filter;
                
    else
                    condition 
    = Expression.Or(condition,filter);
            }

            
    if (condition != null)
                
    return source.Where((Expression<Func<TEntity, bool>>)Expression.Lambda(condition,
    param));

            
    return source;
        }


    使用时,
           var q3 = db.Customers.WhereOr("City", new List<string> { "London", "BeiJing" }).ToList();

     

    恳请帮我解释下,最好能有可编译通过的例子下载。谢谢!

    2011年2月27日 1:27
  • http://msdn.microsoft.com/zh-cn/library/Bb513731(v=VS.90).aspx

     

    微软也是这么说的啊。我咋就运行通不过呢?

    这是我的源码:(RIA模式,从服务器读取数据。表:WorkPlan

                    string[] ids = new string[] { "工作", "工作2" };
                    var queryNames =
                        from ss in myPlanService.GetWorkPlanQuery()
                        where ids.Contains(ss.任务类型)
                        select ss;

                     myPlanService.Load(queryNames);

    错误描述:Query operator 'Contains' is not supported.

     

    2011年2月27日 5:16
  • TFSoft给出的代码一定通过调试,才会贴出来。请你检查一下,你是否拷贝错了。
    2011年2月27日 5:19
  • 我倒不是直接拷过去的。我估计是其它的出问题了。因为微软给的方法就是我提出问题时的方法。

     

    郁闷。再找找。

    2011年2月27日 7:00
  • 找到问题了。应该是粗心大意,随意照抄导致的后果。

    为了偷懒,写了个var,我的是RIA读取数据库中的内容,我那句话其实是定义了一个查询语句,所以它产生的类型是:EntityQuery<G_WorkPlan> ,其实编译器想说的是EntityQuery<G_WorkPlan> 不支持contains吧?

    后面又转换了一下,用的这个IEnumerable<G_WorkPlan>。

    下面是源码。

    EntityQuery<G_WorkPlan>  query=from a from myPlanService.GetG_WorkPlanQuery()   select a;

    myPlanService.load(query);//这里把数据读出来。

    在myPlanService_PropertyChanged中:

    if(e.PropertyName == "IsLoading")

    {

          IEnumerable<G_WorkPlan> a=from b in myPlanService.G_WorkPlans   where  lstStrings.Contains(b.类型.Trim())    select b;

     

    }

    2011年2月27日 7:17