在ASP.NET MVC开发中,经常需要根据用户输入的条件进行数据查询。传统的查询方法通常涉及到大量的if语句,这不仅使得代码难以维护,而且效率也不高。为了解决这个问题,可以使用表达式树(Expression Trees)来简化查询操作。表达式树是一种强大的编程工具,它允许在运行时构建和操作代码。通过将表达式树作为Action的参数,可以动态地构建查询条件,从而提高编程效率和代码的可维护性。
首先,定义一个Employee模型,它将用于本文的示例。
public class Employee {
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Sex { get; set; }
public DateTime? Birthday { get; set; }
public string Remark { get; set; }
}
这个模型包含了员工的基本信息,如ID、姓名、性别、生日和备注。
在MVC中,通常通过Action参数来接收查询条件,然后构建查询语句。以下是一个查询Employee的Action示例:
public ActionResult Index(string firstName, string lastName, DateTime? birthday, bool? sex) {
var employees = repository.Query();
if (!string.IsNullOrEmpty(firstName))
employees = employees.Where(e => e.FirstName.Contains(firstName));
if (!string.IsNullOrEmpty(lastName))
employees = employees.Where(e => e.LastName.Contains(lastName));
if (birthday.HasValue)
employees = employees.Where(e => e.Birthday.Value.Date == birthday.Value.Date);
if (sex.HasValue)
employees = employees.Where(e => e.Sex == sex);
return View(employees);
}
虽然这种方法可以工作,但它存在一些局限性。首先,代码中有很多重复的if语句,这使得代码难以阅读和维护。其次,如果想要添加新的查询条件,需要在多个地方进行修改,这增加了代码的复杂性。
为了解决这些问题,可以使用表达式树作为Action的参数。以下是一个使用表达式树的Action示例:
public ActionResult Index2(string firstName, string lastName, DateTime? birthday, bool? sex) {
var employees = repository.Query()
.WhereIf(e => e.FirstName.Contains(firstName), !string.IsNullOrEmpty(firstName))
.WhereIf(e => e.LastName.Contains(lastName), !string.IsNullOrEmpty(lastName))
.WhereIf(e => e.Birthday.Value.Date == birthday.Value.Date, birthday.HasValue)
.WhereIf(e => e.Sex == sex, sex.HasValue);
return View("Index", employees);
}
在这个示例中,使用了一个自定义的WhereIf扩展方法来简化查询条件的构建。这种方法使得代码更加清晰和易于维护。
然而,MVC的DefaultModelBinder并不能直接绑定表达式树。因此,需要创建一个自定义的ModelBinder来实现这一功能。以下是一个自定义ModelBinder的示例:
public class QueryConditionExpressionModelBinder : IModelBinder {
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
var modelType = GetModelTypeFromExpressionType(bindingContext.ModelType);
if (modelType == null)
return null;
var body = default(Expression);
var parameter = Expression.Parameter(modelType, modelType.Name);
foreach (var property in modelType.GetProperties()) {
var queryValue = GetValueAndHandleModelState(property, bindingContext.ValueProvider, controllerContext.Controller);
if (queryValue == null)
continue;
Expression propertyCondition = null;
if (property.PropertyType == typeof(string)) {
if (!string.IsNullOrEmpty(queryValue as string)) {
propertyCondition = parameter.Property(property.Name).Call("Contains", Expression.Constant(queryValue));
}
} else if (property.PropertyType == typeof(DateTime?)) {
propertyCondition = parameter.Property(property.Name).Property("Value").Property("Date").Equal(Expression.Constant(queryValue));
} else {
propertyCondition = parameter.Property(property.Name).Equal(Expression.Constant(queryValue));
}
if (propertyCondition != null)
body = body != null ? body.AndAlso(propertyCondition) : propertyCondition;
}
if (body == null) body = Expression.Constant(true);
return body.ToLambda(parameter);
}
private Type GetModelTypeFromExpressionType(Type lambdaExpressionType) {
if (lambdaExpressionType.GetGenericTypeDefinition() != typeof(Expression<>))
return null;
var funcType = lambdaExpressionType.GetGenericArguments()[0];
if (funcType.GetGenericTypeDefinition() != typeof(Func<,>))
return null;
var funcTypeArgs = funcType.GetGenericArguments();
if (funcTypeArgs[1] != typeof(bool))
return null;
return funcTypeArgs[0];
}
private object GetValueAndHandleModelState(PropertyInfo property, IValueProvider valueProvider, ControllerBase controller) {
var result = valueProvider.GetValue(property.Name);
if (result == null)
return null;
var modelState = new ModelState { Value = result };
controller.ViewData.ModelState.Add(property.Name, modelState);
object value = null;
try {
value = result.ConvertTo(property.PropertyType);
} catch (Exception ex) {
modelState.Errors.Add(ex);
}
return value;
}
}
这个自定义ModelBinder可以根据请求中的参数自动生成表达式树。这样,就可以在Action中直接使用表达式树作为查询条件了。
本文介绍了如何使用表达式树简化ASP.NET MVC中的查询操作。通过自定义ModelBinder,可以动态地构建查询条件,从而提高编程效率和代码的可维护性。虽然本文的示例代码中有很多硬编码的部分,但它们证明了这种方法的可行性。在后续的文章中,将介绍如何编写更灵活的QueryConditionExpressionModelBinder来处理复杂的查询。希望本文对有所帮助。