协助重构LINQ方法

时间:2015-02-10 21:18:50

标签: c# asp.net linq

我需要一些帮助,我的方法在我的课程中重复了6次,每个方法中唯一改变的是LINQ属性(在示例中#34;名字和# 34;,但我也有一个姓氏,公司名称,用户ID,状态)。我想帮助重构一下这样做,这样我就可以只使用一种方法,使属性变为动态或传入。

private static IQueryable<MyModel> FilterFirstName(IQueryable<MyModel> query, string searchText, string searchFilter)
    {
        switch (searchFilter.ToLower())
        {
            case "contains":
                query = query.Where(x => x.FirstName.ToLower().Contains(searchText.ToLower()));
                break;
            case "does not contain":
                query = query.Where(x => !x.FirstName.ToLower().Contains(searchText.ToLower()));
                break;
            case "starts with":
                query = query.Where(x => x.FirstName.StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase));
                break;
            case "ends with":
                query = query.Where(x => x.FirstName.EndsWith(searchText, StringComparison.InvariantCultureIgnoreCase));
                break;
            case "equals":
                query = query.Where(x => x.FirstName.Equals(searchText, StringComparison.InvariantCultureIgnoreCase));
                break;
        }

        return query;
    }

4 个答案:

答案 0 :(得分:2)

你可以做的是使用Compose方法,可以将一个表达式与另一个表达式组合在一起:

public static Expression<Func<TFirstParam, TResult>>
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}

使用以下方法将一个表达式的所有实例替换为另一个:

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

现在你可以写:

private static IQueryable<MyModel> FilterFirstName(
    IQueryable<MyModel> query,
    Expression<Func<MyModel, string>> selector,
    string searchText,
    string searchFilter)
{
    switch (searchFilter.ToLower())
    {
        case "contains":
            query = query.Where(selector.Compose(
                text => text.ToLower().Contains(searchText.ToLower())));
            break;
        case "does not contain":
            query = query.Where(selector.Compose(
                text => !text.ToLower().Contains(searchText.ToLower())));
            break;
        case "starts with":
            query = query.Where(selector.Compose(
                text => text.StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase)));
            break;
        case "ends with":
            query = query.Where(selector.Compose(
                text => text.EndsWith(searchText, StringComparison.InvariantCultureIgnoreCase)));
            break;
        case "equals":
            query = query.Where(selector.Compose(
                text => text.Equals(searchText, StringComparison.InvariantCultureIgnoreCase)));
            break;
    }

    return query;
}

另外,您应该使用enum来表示searchFilter的不同类型的过滤器,而不是字符串。这将使调用者更容易,因为他们不需要输入精确的字符串,而无需知道确切的选项是什么,或者提供的选项是否有效。

答案 1 :(得分:-1)

此版本允许您将属性传递给过滤器,如下所示:

Filter(models, (MyModel m) => m.FirstName, "Joe", "contains"); 
Filter(models, (MyModel m) => m.LastName, "Smith", "contains"); 

private static IQueryable<MyModel> Filter(IQueryable<MyModel> query, Func<MyModel, string> property, string searchText, string searchFilter)
{
    switch (searchFilter.ToLower())
    {
        case "contains":
            query = query.Where(x => property(x).ToLower().Contains(searchText.ToLower()));
            break;
        case "does not contain":
            query = query.Where(x => !property(x).ToLower().Contains(searchText.ToLower()));
            break;
        case "starts with":
            query = query.Where(x => property(x).StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase));
            break;
        case "ends with":
            query = query.Where(x => property(x).EndsWith(searchText, StringComparison.InvariantCultureIgnoreCase));
            break;
        case "equals":
            query = query.Where(x => property(x).Equals(searchText, StringComparison.InvariantCultureIgnoreCase));
            break;
    }

    return query;
}

答案 2 :(得分:-1)

我会做以下一项或多项:

  1. 反映并缓存属性查找&#34;方法&#34; (&#34; x.FirstName&#34;位可以很容易地通过反射来处理

  2. 选择&#34;选择器&#34;函数并将其应用于拾取目标对象

  3. 将此作为扩展方法

  4. 代码:

    public static class FilterPerson
    {
        static IQueryable<Person> FilterPerson(
           this IQueryable<Person> query,
           FilterString filter,
           Func<Person, string> selector,
            string searchText)
        {
            var enumerableQuery = query.AsEnumerable();
            switch (filter)
            {
                case FilterString.Contains:
                    enumerableQuery = enumerableQuery.Where(x => selector(x).ToLowerInvariant().Contains(searchText.ToLowerInvariant()));
                    break;
                case FilterString.DoesNotContain:
                    enumerableQuery = enumerableQuery.Where(x => !selector(x).ToLower().Contains(searchText.ToLower()));
                    break;
                case FilterString.StartsWith:
                    enumerableQuery = enumerableQuery.Where(x => selector(x).StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase));
                    break;
                case FilterString.EndsWith:
                    enumerableQuery = enumerableQuery.Where(x => selector(x).EndsWith(searchText, StringComparison.InvariantCultureIgnoreCase));
                    break;
                case FilterString.Equals:
                    enumerableQuery = enumerableQuery.Where(x => selector(x).Equals(searchText, StringComparison.InvariantCultureIgnoreCase));
                    break;
            }
    
            return enumerableQuery.AsQueryable();
        }
    }
    
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string PhoneNumber { get; set; }
    
    }
    
    public enum FilterString
    {
        StartsWith,
        Contains,
        DoesNotContain,
        EndsWith,
        Equals
    }
    

答案 3 :(得分:-5)

如果在代码中调用了6次,并且其中有5个路径,我会说方法本身会引入不必要的耦合,应该删除。