2012-01-23 41 views
13

Tôi đang cố gắng xây dựng một biểu thức lambda sẽ được kết hợp với các biểu thức khác thành một cây biểu thức khá lớn để lọc. Điều này làm việc tốt cho đến khi tôi cần phải lọc bởi một thuộc tính bộ sưu tập phụ.Xây dựng một cây biểu thức động để lọc trên thuộc tính bộ sưu tập

Làm thế nào để bạn xây dựng một biểu thức Lambda sẽ lọc bằng cách sử dụng Bất kỳ() trên thuộc tính của một bộ sưu tập là thuộc tính của đối tượng gốc?

Ví dụ:

CurrentDataSource.Offices.Where(o => o.base_Trades.Any(t => t.Name == "test")) 

Đây là cách tôi sẽ xây dựng biểu thức tĩnh nhưng tôi cần phải xây dựng nó tự động. Xin lỗi vì sự nhầm lẫn.

Edit: Đây là một đoạn làm thế nào tôi xử lý các biểu thức ít phức tạp hơn:

IQueryable<Office> officeQuery = CurrentDataSource.Offices.AsQueryable<Office>(); 
ParameterExpression pe = Expression.Parameter(typeof(Office), "Office"); 
ParameterExpression tpe = Expression.Parameter(typeof(Trades), "Trades"); 

Expression SimpleWhere = null; 
Expression ComplexWhere = null; 
foreach (ServerSideFilterObject fo in ssfo) 
{ 
    SimpleWhere = null; 
    foreach (String value in fo.FilterValues) 
    { 
     if (!CollectionProperties.Contains(fo.PropertyName)) 
     { 
      //Handle singleton lambda logic here. 
      Expression left = Expression.Property(pe, typeof(Office).GetProperty(fo.PropertyName)); 
      Expression right = Expression.Constant(value); 
      if (SimpleWhere == null) 
      { 
       SimpleWhere = Expression.Equal(left, right); 
      } 
      else 
      { 
       Expression e1 = Expression.Equal(left, right); 
       SimpleWhere = Expression.Or(SimpleWhere, e1); 
      } 
     } 
     else 
     { 
      //handle inner Collection lambda logic here. 
      Expression left = Expression.Property(tpe, typeof(Trades).GetProperty("Name")); 
      Expression right = Expression.Constant(value); 
      Expression InnerLambda = Expression.Equal(left, right); 

      //Problem area. 
      Expression OfficeAndProperty = Expression.Property(pe, typeof(Office).GetProperty(fo.PropertyName)); 
      Expression OuterLambda = Expression.Call(OfficeAndProperty, typeof(Trades).GetMethod("Any", new Type[] { typeof(Expression) }),InnerLambda); 

      if (SimpleWhere == null) 
       SimpleWhere = OuterLambda; 
      else 
       SimpleWhere = Expression.Or(SimpleWhere, OuterLambda); 
     } 
    } 
    if (ComplexWhere == null) 
     ComplexWhere = SimpleWhere; 
    else 
     ComplexWhere = Expression.And(ComplexWhere, SimpleWhere); 
} 
MethodCallExpression whereCallExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { officeQuery.ElementType }, officeQuery.Expression, Expression.Lambda<Func<Office, bool>>(ComplexWhere, new ParameterExpression[] { pe })); 
results = officeQuery.Provider.CreateQuery<Office>(whereCallExpression); 
+0

Bạn đang hỏi cách xây dựng một cây biểu thức? – SLaks

+0

Tôi không chắc chắn cách cấu trúc phân cấp hoạt động trong ví dụ của bạn. Bạn có thể xây dựng thêm một chút về điều đó không? Văn phòng có phải là gốc và sau đó mỗi Văn phòng có một bộ sưu tập Giao dịch không? Và bạn muốn lọc tên thương mại? Bộ lọc là nơi tôi bị mất một chút. Lấy làm tiếc. –

+0

Không, tôi không chắc chắn về cú pháp được sử dụng để xây dựng một biểu thức với một cuộc gọi phương thức nội bộ và một biểu thức cho một tham số. Trong trường hợp này, tôi nhận được một lỗi nói rằng Any() không thể được tìm thấy bởi vì các tham số của tôi không khớp với định nghĩa. Trong trường hợp này, tôi không chắc chắn nếu đó là vì tôi tắt cú pháp hoặc nếu Any() không được hỗ trợ theo cách tôi đang sử dụng nó. – George

Trả lời

9

Tìm thấy giải pháp. Tôi đã không tìm kiếm bất kỳ phương pháp nào ở đúng nơi trước đây.

Expression left = Expression.Property(tpe, typeof(Trades).GetProperty("Name")); 
Expression right = Expression.Constant(value); 
Expression InnerLambda = Expression.Equal(left, right); 
Expression<Func<Trades, bool>> innerFunction = Expression.Lambda<Func<Trades, bool>>(InnerLambda, tpe); 

method = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any" && m.GetParameters().Length == 2).Single().MakeGenericMethod(typeof(Trades)); 
OuterLambda = Expression.Call(method, Expression.Property(pe, typeof(Office).GetProperty(fo.PropertyName)),innerFunction); 
0

gì bạn liệt kê như ví dụ của bạn sẽ làm việc dựa trên nhận xét của bạn. Dưới đây là ví dụ về những gì tôi làm việc với:

Templates.Where(t => t.TemplateFields.Any(f => f.Required == 'Y')) 

Chúng tôi có các mẫu có bộ sưu tập trường cụ thể và các trường đó có thể được yêu cầu. Vì vậy, tôi có thể nhận được các mẫu mà bất kỳ trường nào được yêu cầu bởi câu lệnh trên.

Hy vọng điều này sẽ giúp ... hoặc ít nhất là xác nhận những gì bạn đang cố gắng làm. Hãy cho tôi biết nếu bạn có thêm câu hỏi về điều này và tôi sẽ giải thích.

Chúc may mắn!

+0

Điều này tương tự như những gì tôi đang làm việc nhưng tôi phải xây dựng biểu thức lamda một cách linh hoạt với sự phản chiếu để tôi có thể đảm bảo bộ lọc kết hợp các bộ lọc khác trong bộ này. – George

0

Các mã được cung cấp

CurrentDataSource.Offices.Where(o => o.base_Trades.Any(t => t.Name == "test")) 

nên làm việc, miễn là o.base_Trades thực hiện IEnumerable<Trade>. Nếu o.base_Trades chỉ thực hiện IEnumerable, bạn cần sử dụng Cast<Trade>() nếu bạn có thể chắc chắn rằng tất cả các phần tử trong o.base_Trades thuộc loại Trade hoặc OfType<Trade>() nếu có thể có các yếu tố khác (không tương thích).

đó sau đó sẽ trông như thế này:

CurrentDataSource.Offices 
    .Where(o => o.base_Trades.Cast<Trade>.Any(t => t.Name == "test")) 
1

Vui lòng không thực hiện việc này, điều bạn thực sự muốn sử dụng thư viện có tên là LINQ động. http://nuget.org/packages/DynamicLINQ

Bạn chỉ có thể lưu trữ truy vấn dưới dạng chuỗi và hỗ trợ truy vấn rất phức tạp. Cây biểu hiện là một cơn ác mộng.

+0

đó là một thư viện tuyệt vời, tôi sử dụng nó quá, nhưng nó không hỗ trợ ví dụ phân loại trên một tài sản của một thuộc tính bộ sưu tập như 'myIQueryable.OrderBy (x => x.MyCollection.Select (y => y.Myproperty)) ', ít nhất tôi không thể làm cho nó hoạt động –

Các vấn đề liên quan