Expression<Func<Student, bool>> filter=s=>s.Name.Contains("a") && s.Age>=20;
這樣的表達試轉換成node
Expression<Func<DataRow, bool>> filter = r=>((string)r["Name"]).Contains("a") && ((int)r["Age"])>=20;
也許你會問,幹嗎要這樣作呢?舉個例子,數據庫
說DAL裏有一個類StudentProvider用於對student進行數據庫的增刪改查的操做。咱們就拿查詢來講,查詢能夠有不少的條件。以往可能會有相似的方法:ide
public IEnumerable<Student> GetStudentsByName(string name); public Student GetStudentById(int id);
可是別忘了今天的世界有了Expression,咱們應該向這些落後的(別打我,竊覺得的)方法說再見了。高顏值的接口固然要寫成這樣了:spa
public IEnumerable<Student> GetStudents(Expression<Func<Student, bool>> filter);
因而咱們來看看這個方法的實現,code
public IEnumerable<Student> GetStudents(Expression<Func<Student, bool>> filter) { using (var connection=new SqlConnection("some connection string")) { var selectSql = "SELECT * FROM Student"; using (var adapter = new SqlDataAdapter(selectSql, connection)) { var ds = new DataSet(); adapter.Fill(ds, "table"); return from raw in ds.Tables["table"].AsEnumerable() select new Student(raw); } } }
實現用到了Linq to DataSet, 其實咱們真正想作的是blog
return from raw in ds.Tables["table"].AsEnumerable().Where(filter) select new Student(raw)
可是問題是Where只接受接口
Func<DataRow, bool> predicate
到這裏,終於明白了爲何要作LambdaExpression變換了吧。get
前一篇中咱們看到了ExpressionVisitor的強大,這裏咱們還要用他來解決問題。咱們引入一個ConvertMemberToColumnVisitor:string
public class ConvertMemberToColumnVisitor : ExpressionVisitor { private readonly Expression _columnOwnerExpression; private readonly string _memberOwnerName; public ConvertMemberToColumnVisitor(Expression columnOwnerExpression, string memberOwnerName) { _columnOwnerExpression = columnOwnerExpression; _memberOwnerName = memberOwnerName; } protected override Expression VisitMember(MemberExpression node) { var parameterExpression = node.Expression as ParameterExpression; if (parameterExpression != null && parameterExpression.Name == _memberOwnerName) { return Expression.Convert(Expression.Call(_columnOwnerExpression, typeof(DataRow).GetMethod("get_Item", new []{typeof(string)}), Expression.Constant(node.Member.Name)), ((PropertyInfo)node.Member).PropertyType); } return base.VisitMember(node); } }
很簡單,很定一個咱們要替代成的表達式,固然咱們仍是用parameter name來匹配因此要給定一個參數名。it
有了這個Visitor後,一切問題都簡單了:
public IEnumerable<Student> GetStudents(Expression<Func<Student, bool>> filter) { using (var connection=new SqlConnection("some connection string")) { var selectSql = "SELECT * FROM Student"; using (var adapter = new SqlDataAdapter(selectSql, connection)) { var ds = new DataSet(); adapter.Fill(ds, "table"); var p1 = Expression.Parameter(typeof(DataRow), "r"); var converter = new ConvertMemberToColumnVisitor(p1, filter.Parameters[0].Name); var newExp = converter.Visit(filter); var lambda = Expression.Lambda<Func<DataRow, bool>>(((LambdaExpression)newExp).Body, p1); var predicate = lambda.Compile(); return from raw in ds.Tables["table"].AsEnumerable().Where(predicate) select new Student(raw); } } }