表达式LambdaVisual Studio 2012.NET4.5Entity FrameworkVisual Studio 2010LINQ.NET4初学者C# 3.5中级开发Visual Studio.NET
动态创建谓词委托





5.00/5 (5投票s)
创建了一个概念验证 (POC),使用表达式和反射动态构建谓词。
引言
最近,我完成了一个任务,涉及到创建一个小的搜索方法。该应用程序使用 ORM 和 LINQ 来查询数据库。我决定研究构建表达式树,在构建了 POC 并证明动态构建谓词效果良好后,我完成了我的任务。我决定上传我的 POC。如果我能帮助下一个遇到类似项目的人,那就太好了。对于一些人来说,这可能非常基础,对于另一些人来说,这是一个很好的起点。
使用代码
代码使用一个通用的 User 类列表(实体/模型)来进行搜索,以及一个 Search 类,在其中构建动态表达式树并创建谓词。在上传的文件中,我创建了一个控制台应用程序,将所有这些整合在一起。以下是 Search 类的内容,用于构建谓词,以及要在列表中搜索的 User 类。我还上传了我的 POC。
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; namespace MyPredicateBuilder { #region Search Class public class Search { private List<User> _users; public Search(List<User> users) { _users = users; } public List<User> Results { get; private set; } public List<User> For(string firstName = "", string lastName = "", string email = "", int? userId = null) { var result = new List<User>(); //start building expression tree // Starting out with specifying the Entity/Model to be searched within the generic list. // In this case, we start with User list. var li = Expression.Parameter(typeof(User), "User"); Expression where = null; if (!String.IsNullOrEmpty(firstName)) { AndAlso(NameSearch(li, "First", firstName), ref where); } if (!String.IsNullOrEmpty(lastName)) { AndAlso(NameSearch(li, "Last", lastName), ref where); } if (!String.IsNullOrEmpty(email)) { AndAlso(EmailSearch(li, email), ref where); } if (userId.HasValue) { AndAlso(UserIdSearch(li, userId.Value), ref where); } if (where == null) { return _users; } var predicate = Expression.Lambda<Func<User, bool>>(where, new ParameterExpression[] { li }).Compile(); return _users.Where(predicate).ToList(); } private void AndAlso(Expression equals, ref Expression op) { if (op == null) { op = equals; } else { op = Expression.AndAlso(op, equals); } } /// <summary> /// Search first/last user name /// </summary> /// <param name="listOfNames"></param> /// <param name="propertyName"></param> /// <param name="value"></param> /// <returns></returns> private Expression NameSearch(Expression listOfNames, string propertyName, string value) { // a. get expression for Name object within list var nameObjInList = Expression.Property(listOfNames, "Name"); // b. get expression of property found in Name object var nameProperty = Expression.Property(nameObjInList, propertyName); // c. get MethodInfo of the StartWith method found in the String object var startsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) }); // d. get expression that represents the name value to search var nameSearch = Expression.Constant(value, typeof(string)); // e. get the start with expression call var startWithCall = Expression.Call(nameProperty, startsWithMethod, new Expression[1] { nameSearch }); // f. get expression that represents the value for the StartWith return upon executing var right = Expression.Constant(true, typeof(bool)); // g. return final equals expression to help build predicate return Expression.Equal(startWithCall, right); } /// <summary> /// Search email within list of emails /// </summary> /// <param name="listOfNames"></param> /// <param name="value"></param> /// <returns></returns> private Expression EmailSearch(Expression listOfNames, string value) { //a. Create expression predicate. Expression<Predicate<string>> emailPred = e => e.StartsWith(value); //b. Create expression property for User propery Emails list var ep = Expression.Property(listOfNames, "Emails"); //c. Create constant value of return var isTrue = Expression.Constant(true, typeof(bool)); //d. Get methodinfo for string List<string>.Exists(..) method var existsMethod = typeof(List<string>).GetMethod("Exists", new[] { typeof(Predicate<string>) }); //e. Create expression call var existsCall = Expression.Call(ep, existsMethod, new Expression[1] { emailPred }); //f. return comparison expression return Expression.Equal(existsCall, isTrue); } private Expression UserIdSearch(Expression listOfNames, int userId) { var userIdParam = Expression.Property(listOfNames, "UserId"); var userIdValue = Expression.Constant(userId, typeof(int)); return Expression.Equal(userIdParam, userIdValue); } } #endregion #region User Entity public class UserName { public string First { get; set; } public string Last { get; set; } } public class User { public User() { Emails = new List<String>(); Name = new UserName(); } public int UserId { get; set; } public UserName Name { get; set; } public List<string> Emails { get; set; } } #endregion }
关注点
这是我第一次使用 Expression 对象和反射。在进一步研究后,我还看到了一个 PredicateBuilder,网址:http://www.albahari.com/nutshell/predicatebuilder.aspx。这绝对简化了执行完全相同操作的过程。说我疯了,但我喜欢深入研究(偶尔),尤其是当所需的解决方案简单明了,并且我有时间开发它时。