我看到很多代码类似于以下
var customrs = MyDataContext.Customers.Where(...);
if (!String.IsNullOrEmpty(input)) {
customers = customers.Where(c => c.Email.Contains(input));
}
我想把它放在一个扩展方法中,该方法在调用IQueryable上的Where之前检查输入是否有效,以便可以像
一样调用它customers = MyDataContext.Customers.Where(...)
.ContainsText(c => c.Email, input);
我的扩展方法如下所示
public static IQueryable<T> ContainsText<T>(this IQueryable<T> source, Expression<Func<T, string>> selector, string text) {
if (String.IsNullOrEmpty(text) {
return source;
}
else {
//do something here
}
}
如何在解析的表达式上调用Contains()?或者是否有另一种方法可以返回结果的IQueryable,其中解析的表达式包含解析的文本?
更新:适用于Linq to Sql
答案 0 :(得分:4)
tvanfosson有一个正确的想法,即构建导致this answer到this question的表达式。因此,为了完整起见,这是一个完整的解决方案
表达式构建器
public static class ExpressionBuilder {
public static Expression<Func<T, bool>> ContainsText<T>(string propertyName, string text) {
var paramExp = Expression.Parameter(typeof(T), "type");
var propExp = Expression.Property(paramExp, propertyName);
var methodInfo = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var valueExp = Expression.Constant(text, typeof(string));
var methCall = Expression.Call(propExp, methodInfo, valueExp);
return Expression.Lambda<Func<T, bool>>(methCall, paramExp);
}
}
扩展方法
public static class IQueryableExtensions {
public static IQueryable<T> ContainsText<T>(this IQueryable<T> source, Expression<Func<T, string>> selector, string text) {
if (source == null) {
throw new ArgumentNullException();
}
if (text.IsNullOrEmpty()) {
return source;
}
string propName = ((MemberExpression)selector.Body).Member.Name;
return source.Where(ExpressionBuilder.ContainsText<T>(propName, text));
}
}
像
一样调用var customers = MyDataContext.Customers.Where(/* some code */)
.ContainsText(c => c.Email, input);
答案 1 :(得分:3)
我想我会在String上进行扩展,而不是在IQueryable上。
public static bool ContainsIfNotEmpty( this string source, string text )
{
if (string.IsNullOrEmpty(text))
{
return true; // select all elements
}
if (string.IsNullOrEmpty(source))
{
return false; // select no elements
}
return source.Contains(text); // select only matching
}
然后将其用作:
customers = MyDataContext.Customers
.Where( c => c.Email.ContainsIfNotEmpty( input ) );
请注意,这需要LINQ to对象。如果需要将它与LINQ to SQL一起使用,那么我建议使用构建器方法构建表达式。请注意以下内容未经测试,因为我现在无权访问VS.您可能需要查看Andrew Peters的blog entry以获取类似的示例和/或Expression类的文档。
public static class ExpressionBuilders
{
public static Expression<Func<T,bool>> ContainsBuilder<T>( string column, string text )
{
ParameterExpression parameter = new Expression.Parameter( typeof(T), "t" );
if (string.IsNullOrEmpty(text))
{
return (Expression<Func<T,bool>>)QueryExpression.Lambda( Expression.Constant( true ), parameter );
}
MethodInfo contains = typeof(T).GetMethod("Contains");
Expression textExpression = Expression.Constant(text);
Expression containsExpression = Expression.Call(parameter,contains,textExpression);
return (Expression(Func<T,bool>))QueryExpression.Lambda( containsExpression, parameter );
}
}
用作:
var predicate = ExpressionBuilders.ContainsBuilder<Customer>( "Email", input );
customers = MyDataContext.Customers.Where( predicate );
答案 2 :(得分:1)
使用IEnumerable<T>
:
public static IEnumerable<T> ContainsText<T>(
this IEnumerable<T> source, Func<T, string> selector, string text)
{
return source.Where(
x => (!string.IsNullOrEmpty(selector(x)) &&
(selector(x).Contains(text))));
}
为了留在IQueryable<T>
你唯一的选择是动态Linq,如tvanfosson建议或User Defined Functions。
但说实话,你可能还会留在Where
。
答案 3 :(得分:0)
我认为你可以这样做:
customers = MyDataContext.Customers.Where(c => (string.IsNullOrEmpty(input) || c.Email.Contains(input)));