c# - 如何重构此代码以进行 LINQ 过滤?

标签 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;
    }
}    

最佳答案

您可以提取此方法:

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));

如果您不想逐个搜索每个属性并且想要检查类的任何属性是否回答搜索,则另一种选择是使用反射来迭代类的属性并检查是否有任何属性回答条件:

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,但我建议,它比在数据库中执行所有过滤(如第一个查询那样)的效率要低尽管保留您的第一个查询具有可读性。

关于c# - 如何重构此代码以进行 LINQ 过滤?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40190927/

相关文章:

c# - 具有键 'IDMateria' 的 ViewData 项的类型为 'System.Int32' 但必须为 'IEnumerable<SelectListItem>' 类型

c# - 将 .Net Windows 移植到 Linux

c# - LINQ Group By multiple values 在 VB.NET 中效果不佳,但在 C# 中效果很好

c# - 字符串中某个字符出现的次数

c# - 使用线程时未获取更新的数据

c# - 使用连接字符串连接到数据库以使用 LINQ

c# - 如何在 MVVM 中处理动态按钮中的执行和可以执行条件

c# - 绑定(bind)到 ListBox 中已排序的 ObservableCollection<T>

c# - RadGrid 在细节表扩展时刷新

c# - 检查是否没有返回数据行