2013-05-29 89 views
13

Giả sử tôi có một cái gì đó giống nhưTừ BinaryExpression để Expression <Func <T, bool>>

Expression<Func<SomeType, DateTime>> left = x => x.SomeDateProperty; 
Expression<Func<SomeType, DateTime>> right = x => dateTimeConstant; 
var binaryExpression = Expression.GreaterThan(left, right); 
Expression<Func<SomeType, bool>> predicate = 
          x => x.SomeDateProperty> dateTimeConstant; 

1) Làm thế nào tôi có thể thay thế bên hữu sự phân công của dòng cuối cùng với một cái gì đó mà sử dụng binaryExpression để thay thế? var predicate = x => binaryExpression; không hoạt động.

2) right luôn là hằng số, không nhất thiết là DateTime.Now. Nó có thể thuộc loại đơn giản hơn Expression không? Ví dụ, nó không phụ thuộc vào SomeType, nó chỉ là một hằng số.

3) Nếu tôi có GreaterThanstring, có cách nào để lấy từ chuỗi này đến phương thức có cùng tên trong Expression không? Nói chung, nếu tên của phương pháp so sánh được đưa ra là string, làm cách nào tôi có thể đi từ chuỗi để thực sự gọi phương thức có cùng tên trên lớp Expression?

Nó phải hoạt động với LINQ to Entities, nếu nó quan trọng.

+0

Bạn có thể thay đổi cây biểu thức bằng cách sử dụng lớp ExpressionVisitor. – Steven

Trả lời

13

1 và 2: Bạn cần xây dựng cây biểu thức theo cách thủ công để thực hiện việc này, trình biên dịch không thể trợ giúp vì trình biên dịch chỉ xây dựng các biểu thức được thực hiện đại diện cho các hàm trong toàn bộ. Điều đó không hữu ích khi bạn muốn xây dựng các chức năng từng mảnh một.

Dưới đây là một trong những cách đơn giản để xây dựng các biểu hiện bạn muốn:

var argument = Expression.Parameter(typeof(SomeType)); 
var left = Expression.Property(argument, "SomeDateProperty"); 
var right = Expression.Constant(DateTime.Now); 

var predicate = Expression.Lambda<Func<SomeType, bool>>(
    Expression.GreaterThan(left, right), 
    new[] { argument } 
); 

Bạn có thể thực hiện việc này cho một ổ đĩa thử nghiệm với

var param = new SomeType { 
    SomeDateProperty = DateTime.Now.Add(TimeSpan.FromHours(-1)) 
}; 

Console.WriteLine(predicate.Compile()(param)); // "False" 

3: Vì trong tất cả các khả năng các số lựa chọn có thể cho vị từ nhị phân của bạn sẽ khá nhỏ, bạn có thể làm điều này bằng từ điển:

var wordToExpression = 
    new Dictionary<string, Func<Expression, Expression, BinaryExpression>> 
{ 
    { "GreaterThan", Expression.GreaterThan }, 
    // etc 
}; 

Sau đó, thay vì mã hóa cứng Expression.GreaterThan trong đoạn đầu tiên, bạn sẽ làm điều gì đó như wordToExpression["GreaterThan"](left, right).

Tất nhiên điều này cũng có thể được thực hiện theo cách tiêu chuẩn với sự phản chiếu.

7

Khi bạn sử dụng GreaterThan, bạn cần chỉ định biểu thức các đối tượng, không phải chính lambda. Thật không may, có một biến chứng: x trong hai biểu thức không giống nhau.

Trong đặc biệt trường hợp này, bạn có thể chỉ là về lấy đi này, vì biểu thức thứ hai không sử dụng x

Vì vậy; bạn "1" và "2" cần được trả lời bởi:

var binaryExpression = Expression.GreaterThan(left.Body, right.Body); 
    var lambda = Expression.Lambda<Func<SomeType, bool>>(binaryExpression, 
     left.Parameters); 

Tuy nhiên, để xử lý này trong trường hợp chung, bạn phải viết lại trên của biểu thức, để sửa chữa-up thông số:

var binaryExpression = Expression.GreaterThan(left.Body, 
    new SwapVisitor(right.Parameters[0], left.Parameters[0]).Visit(right.Body)); 
var lambda = Expression.Lambda<Func<SomeType, bool>>(binaryExpression, 
    left.Parameters); 

với:

public class SwapVisitor : ExpressionVisitor 
{ 
    private readonly Expression from, to; 
    public SwapVisitor(Expression from, Expression to) 
    { 
     this.from = from; 
     this.to = to; 
    } 
    public override Expression Visit(Expression node) 
    { 
     return node == from ? to : base.Visit(node); 
    } 
} 

Đối với bạn "3"; không có gì sẵn có cho điều đó; bạn có thể sử dụng sự phản chiếu, mặc dù:

string method = "GreaterThan"; 

var op = typeof(Expression).GetMethod(method, 
    BindingFlags.Public | BindingFlags.Static, 
    null, new[] { typeof(Expression), typeof(Expression) }, null); 

var rightBody = new SwapVisitor(right.Parameters[0], 
    left.Parameters[0]).Visit(right.Body); 
var exp = (Expression)op.Invoke(null, new object[] { left.Body, rightBody }); 

var lambda = Expression.Lambda<Func<SomeType, bool>>(exp, left.Parameters);