LINQ string[] against multiple fields

后端 未结 6 511
天命终不由人
天命终不由人 2021-01-02 20:59

Lets say I have a table dataContext.Customer with the following fields

    FName    varchar
    LName    varchar
    Phone    varchar
    DOB      datetime
          


        
6条回答
  •  星月不相逢
    2021-01-02 21:26

    Using the PredicateBuilder

    void Main()
    {
        var search = new string[] { "Romania","RO"};
        var query = from c in countries.AllAny(search)
            orderby c.name
            select c;
        query.Dump();
    }
    
    public static class QueryExtensions
    {
        public static IQueryable AllAny(this IQueryable query, string[] search)    
        {           
            var properties = typeof(T).GetProperties().Where(p => p.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute),true).Any()).Select(n=>n.Name);        
            var andPredicate = PredicateBuilder.True();
            foreach ( var term in search )
            {
                var orPredicate = PredicateBuilder.False();
                foreach (var property in properties )
                    orPredicate = orPredicate.Or(CreateLike(property,term));
                andPredicate = andPredicate.And(orPredicate);
            }
            return query.Where(andPredicate);
        }
        private static Expression> CreateLike( PropertyInfo prop, string value)
        {       
            var parameter = Expression.Parameter(typeof(T), "f");
            var propertyAccess = Expression.MakeMemberAccess(parameter, prop);            
            var toString = Expression.Call(propertyAccess, "ToString", null, null);
            var like = Expression.Call(toString, "Contains", null, Expression.Constant(value,typeof(string)));
    
            return Expression.Lambda>(like, parameter);       
        }
    
        private static Expression> CreateLike( string propertyName, string value)
        {
            var prop = typeof(T).GetProperty(propertyName);     
            return CreateLike(prop, value);
        }
    
    }
    
    // http://www.albahari.com/nutshell/predicatebuilder.aspx
    public static class PredicateBuilder
    {
      public static Expression> True ()  { return f => true;  }
      public static Expression> False () { return f => false; }
    
      public static Expression> Or (this Expression> expr1,
                                                          Expression> expr2)
      {
        var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast ());
        return Expression.Lambda>
              (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
      }
    
      public static Expression> And (this Expression> expr1,
                                                           Expression> expr2)
      {
        var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast ());
        return Expression.Lambda>
              (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
      }
    }
    

    Update This code is a generic solution for the following query

    from c in countries
    where (c.name.ToString().Contains(search[0]) || c.name.ToString().Contains(search[1]))
        && (c.iso_code.ToString().Contains(search[0]) || c.iso_code.ToString().Contains(search[1]))
        /*&& ...*/
    orderby c.name
    select c
    

    This code can be improved in many ways. For sample, for the string properties, there is no need to call ToString before Contains ( this will generate a convert(nvarchar)) and I really think someone who needs this will only want to look at the varchar, nvarchar columns.

提交回复
热议问题