如何为LINQ过滤重构此代码?

时间:2016-10-22 10:15:44

标签: c# linq

使用DataTables(用于jQuery的表插件)和服务器端处理我必须为我的数据创建具有良好性能的过滤。我这样做但在我看来它有点难看。 (.Where(...))的一部分,我必须手动将每个属性与search变量进行比较。有可能让它变得更好吗?

using System.Linq.Dynamic; // because of special .OrderBy

public class SomeRepository
{
    public DataTableDTO GetAllFromBase(int start, int length, string sortColumn, string sortColumnDir, string search)
    {
        var dataFiltered = db.User
            .AsNoTracking()
            .Select(x => new { x.Id, x.FirstName, x.LastName, x.Description})
            .OrderBy(sortColumn + " " + sortColumnDir)
            .Where(search.Length > 0, x => x.Id.ToString().Contains(search.ToLower())
                                        || x.FirstName.ToLower().Contains(search.ToLower())
                                        || x.LastName.ToLower().Contains(search.ToLower())
                                        || x.Description.ToLower().Contains(search.ToLower()));

        var recordsFiltered = dataFiltered.Count();
        var recordsTotal = db.User.Count();

        var dataToShow = dataFiltered
            .Skip(start)
            .Take(length)
            .ToList();

        var dataForTable = new DataTableDTO
        {
            Data = dataToShow,
            RecordsTotal = recordsTotal,
            RecordsFiltered = recordsFiltered
        };

        return dataForTable;
    }
}

public static class LinqExtensions
{
    public static IQueryable<T> Where<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause)
    {
        if (condition)
        {
            return query.Where(whereClause);
        }
        return query;
    }
}    

1 个答案:

答案 0 :(得分:3)

您可以提取此方法:

public bool SearchForMatching(string source, string serach)
{
    return source.ToLower().Contains(search.ToLower());
} 

它将简化你的Where子句:

Where(x => SearchForMatching(x.Id, search)
           || SearchForMatching(x.FirstName, search)
           || SearchForMatching(x.LastName, search)
           || SearchForMatching(x.Description, search));

另一种选择,如果您不想逐个搜索每个属性,并且想要检查是否有任何类别的问题回答了搜索,那就是使用反射来迭代课程&#39 ; s属性并检查是否有任何一个符合条件:

public bool SearchForMatching(User user, string search)
{
    return user.GetType().GetPropeties().Any(propertyInfo => propertyInfo.GetValue(user).ToString().ToLower().Contains(search.ToLower()));
}

然后在Where子句中使用此方法:

Where(x => SearchForMatching(x, search));

或者只是将它们组合在一起:

Where(x => x.GetType().GetPropeties().Any(propertyInfo => propertyInfo.GetValue(x).ToString().ToLower().Contains(search.ToLower())); 

修改
这两个选项应该可以正常使用LINQ to Objects但它可能无法与LINQ to Entities很好地协作,因为不可能使用LINQ to Entities将第一个选项或反射选项转换为SQL。 /> 您可以使用db.User.AsEnumerable()将所有数据加载到内存中,然后使用任何这些选项使用LINQ to Objects,但它比在第一个查询中执行数据库中的所有过滤效率低,我建议尽管它具有可读性以保持您的第一个查询。