2013-03-23 18 views
16

tanımlı değil Bu kodla dinamik olarak bir LINQ sorgusu yapıyorum. İşe gibi görünüyor, ama benim aramada birden fazla searchString varken birden ifadeler eklenir, bu nedenle zaman (i aşağıdaki hatayı alıyorum:LINQ ifadeleri. Değişken 'p', kapsamdan başvurulan tür ancak

Variable 'p' of type referenced from scope, but it is not defined**

Ben sadece bir kez/kullanımı p tanımlayabilirsiniz sanırım. fakat, eğer öyleyse, sana ben değil, Ürün vb dizeyi kullanın (yapmaya çalışan birkaç satır burada, basitleştirme

if (searchStrings != null) 
    { 
     foreach (string searchString in searchStrings) 
     { 
      Expression<Func<Product, bool>> containsExpression = p => p.Name.Contains(searchString); 
      filterExpressions.Add(containsExpression); 
     } 
    } 

    Func<Expression, Expression, BinaryExpression>[] operators = new Func<Expression, Expression, BinaryExpression>[] { Expression.AndAlso }; 
    Expression<Func<Product, bool>> filters = this.CombinePredicates<Product>(filterExpressions, operators); 

    IQueryable<Product> query = cachedProductList.AsQueryable().Where(filters); 

    query.Take(itemLimit).ToList(); << **error when the query executes** 


    public Expression<Func<T, bool>> CombinePredicates<T>(IList<Expression<Func<T, bool>>> predicateExpressions, Func<Expression, Expression, BinaryExpression> logicalFunction) 
    { 
     Expression<Func<T, bool>> filter = null; 

     if (predicateExpressions.Count > 0) 
     { 
      Expression<Func<T, bool>> firstPredicate = predicateExpressions[0]; 
      Expression body = firstPredicate.Body; 
      for (int i = 1; i < predicateExpressions.Count; i++) 
      { 
       body = logicalFunction(body, predicateExpressions[i].Body); 
      } 
      filter = Expression.Lambda<Func<T, bool>>(body, firstPredicate.Parameters); 
     } 

     return filter; 
    } 
+0

Tam anlamıyorum. Görünüşe göre, 'CombinePredicates' 'n' ifadeleri ve' n-1' operatörlerini bekliyor. Ancak, onu çağırdığınız yerde, '1' uzunluğuna sahip bir dizi operatörünüz var. Katılmak için '2' 'den fazla yüklemenin olması durumunda dizinin sınırlarının dışına çıkma istisnası beklerdim. –

+0

Bunu gördüm, örneğimi daha kompakt hale getirmek için bazı şeyler aldım. Ama bu kısmı teknik olarak doğru yapmak için sorumu değiştireceğim. – Tys

+0

Bu kısmı düzelttim.Ama yine de sorun olduğu gibi kalır. – Tys

cevap

31

? burada kimse bana doğru yönde işaret edebilir. benim kod biraz değiştirmek gerekir vardır ancak fikir aynıdır):

 Expression<Func<string, bool>> c1 = x => x.Contains("111"); 
     Expression<Func<string, bool>> c2 = y => y.Contains("222"); 
     var sum = Expression.AndAlso(c1.Body, c2.Body); 
     var sumExpr = Expression.Lambda(sum, c1.Parameters); 
     sumExpr.Compile(); // exception here 

Foreach'inizi x ve y ile iki ifadeye nasıl genişletdiğimi lütfen unutmayın - bu, derleyici için tam olarak nasıl göründüğü gibi, farklı parametreleridir. Başka bir deyişle

, böyle bir şey yapmaya çalışıyoruz: 'y' değişken şey nedir merak

x => x.Contains("...") && y.Contains("..."); 

ve derleyici ??

Düzeltmek için, tüm ifadeler için tam olarak aynı parametreyi (yalnızca ad değil, aynı zamanda referans) kullanmamız gerekir. Eğer bekleyebilir

222 should not match 
111 222 should match 

Değil şey: çıkışını

internal static class Program 
{ 
    public class Product 
    { 
     public string Name; 
    } 

    private static void Main(string[] args) 
    { 
     var searchStrings = new[] { "111", "222" }; 
     var cachedProductList = new List<Product> 
     { 
      new Product{Name = "111 should not match"}, 
      new Product{Name = "222 should not match"}, 
      new Product{Name = "111 222 should match"}, 
     }; 

     var filterExpressions = new List<Expression<Func<Product, bool>>>(); 
     foreach (string searchString in searchStrings) 
     { 
      Expression<Func<Product, bool>> containsExpression = x => x.Name.Contains(searchString); // NOT GOOD 
      filterExpressions.Add(containsExpression); 
     } 

     var filters = CombinePredicates<Product>(filterExpressions, Expression.AndAlso); 

     var query = cachedProductList.AsQueryable().Where(filters); 

     var list = query.Take(10).ToList(); 
     foreach (var product in list) 
     { 
      Console.WriteLine(product.Name); 
     } 
    } 

    public static Expression<Func<T, bool>> CombinePredicates<T>(IList<Expression<Func<T, bool>>> predicateExpressions, Func<Expression, Expression, BinaryExpression> logicalFunction) 
    { 
     Expression<Func<T, bool>> filter = null; 

     if (predicateExpressions.Count > 0) 
     { 
      var firstPredicate = predicateExpressions[0]; 
      Expression body = firstPredicate.Body; 
      for (int i = 1; i < predicateExpressions.Count; i++) 
      { 
       body = logicalFunction(body, Expression.Invoke(predicateExpressions[i], firstPredicate.Parameters)); 
      } 
      filter = Expression.Lambda<Func<T, bool>>(body, firstPredicate.Parameters); 
     } 

     return filter; 
    } 
} 

Ama dikkat edin: gibi olurdu size orijinal kodu sabitleme, Yani

 Expression<Func<string, bool>> c1 = x => x.Contains("111"); 
     Expression<Func<string, bool>> c2 = y => y.Contains("222"); 
     var sum = Expression.AndAlso(c1.Body, Expression.Invoke(c2, c1.Parameters[0])); // here is the magic 
     var sumExpr = Expression.Lambda(sum, c1.Parameters); 
     sumExpr.Compile(); //ok 

: Böyle bu basitleştirilmiş bir kod çözebilirsiniz .. Bu foreach içinde searchString kullanmanın sonucudur, aşağıdaki şekilde yeniden yazılmalıdır:

111 222 should match 
+0

Ayrıntılarınız için teşekkürler. Ben biraz daha fazla üzerinde çalıştı ve şimdi 'cachedProductList' gerçekten HttpContext.Current.Cache gelen zaman, ben de zaman zaman kapsamından başvurulan türü Değişken 'x' almak olsun, ancak fark etmedi ki fark ettim tanımladı. Önbelleğe almayı kullanmadığımda her şey iyi çalışıyor. Bunun neden olduğu hakkında bir fikrin var mı? – Tys

+0

Önbellekteki (i.e. ... smthing.ToList()) veya her seferinde yürüten IEnumerable'da gerçek sonuç var mı? İlk seçeneğiniz varken ikinci seçeneğiniz var gibi görünüyor .. – Lanorkin

+0

Nope, tüm ürünlerin listesini önbellekte saklıyorum. Ve bundan sonra bu listede arama yapmak istiyorum. – Tys

1

IMHO, listesini yapmak gerek:

var filterExpressions = new List<Expression<Func<Product, bool>>>() 

kolaylıkla Ziyaretçi sınıfında aşağıdaki yaşayacağından:

public class FilterConverter : IFilterConverterVisitor<Filter> { 

    private LambdaExpression ConditionClausePredicate { get; set; } 
    private ParameterExpression Parameter { get; set; } 

    public void Visit(Filter filter) { 

     if (filter == null) { 
      return; 
     } 

     if (this.Parameter == null) { 
      this.Parameter = Expression.Parameter(filter.BaseType, "x"); 
     } 

     ConditionClausePredicate = And(filter); 
    } 

    public Delegate GetConditionClause() { 

     if (ConditionClausePredicate != null) { 

      return ConditionClausePredicate.Compile(); 
     } 

     return null; 
    } 

    private LambdaExpression And(Filter filter) { 

     if (filter.BaseType == null || string.IsNullOrWhiteSpace(filter.FlattenPropertyName)) { 

      //Something is wrong, passing by current filter 
      return ConditionClausePredicate; 
     } 

     var conditionType = filter.GetCondition(); 
     var propertyExpression = filter.BaseType.GetFlattenPropertyExpression(filter.FlattenPropertyName, this.Parameter); 

     switch (conditionType) { 

      case FilterCondition.Equal: { 

       var matchValue = TypeDescriptor.GetConverter(propertyExpression.ReturnType).ConvertFromString(filter.Match); 
       var propertyValue = Expression.Constant(matchValue, propertyExpression.ReturnType); 
       var equalExpression = Expression.Equal(propertyExpression.Body, propertyValue); 
       if (ConditionClausePredicate == null) { 
        ConditionClausePredicate = Expression.Lambda(equalExpression, this.Parameter); 
       } else { 
        ConditionClausePredicate = Expression.Lambda(Expression.And(ConditionClausePredicate.Body, equalExpression), this.Parameter); 
       } 
       break; 
      } 
     // and so on... 
    } 
} 

Ve burada çıkışı Kod optimal değil, biliyorum, yeni başlayan biriyim ve uygulanacak çok şey var ... Ama bu şeyler işe yaramıyor. Buradaki fikir, Ziyaretçi sınıfı başına tek ParameterExpression'a sahip olmak ve daha sonra bu parametreyi kullanarak ifadeler oluşturmaktır. Ardından, bir LambdaExpression maddesi başına tüm ifadeleri birleştirip gerektiğinde delege etmek için derleyin.

İlgili konular