Lamda Expression

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); } } } 
复制代码
相关文章
相关标签/搜索