c# - 如何使用多个条件和 LINQ 过滤列表中的数据?

标签 c# winforms linq

我创建此表单是为了生成一个学生列表,该列表能够按某些条件(左侧)进行过滤并显示所需的任何信息(右侧)
List generation Form

当表格开始初始化时,我用 Entity Framework 获取整个学生列表

List<Student> students = await context.Students.ToListAsync().ConfigureAwait(false);

我将它保存到两个列表中:

private List<Student> _listOfAllStudents = new List<Student>();
private List<Student> _filteredStudents = new List<Student>();

然后我对这样的列表执行我的逻辑:

private void PrepareFilteredStudentListAccordingToFilterCheckedBoxes()
{
    _filteredStudents = _listOfAllStudents;

    if (ColonieFilterCheckBox.Checked)
    {
        _filteredStudents = _filteredStudents.Intersect(_listOfAllStudents.Where(x => x.Colonie).Select(x => x).ToList()).ToList();
    }

    if (NatationFilterCheckBox.Checked)
    {
        _filteredStudents = _filteredStudents.Intersect(_listOfAllStudents.Where(x => x.Nataion).Select(x => x).ToList()).ToList();
    }

    if (ExcursionFilterCheckBox.Checked)
    {
        _filteredStudents = _filteredStudents.Intersect(_listOfAllStudents.Where(x => x.Excursion).Select(x => x).ToList()).ToList();
    }

    //Rest of the code is omitted but you get the idea...
}  

根据显示复选框完成相同的逻辑:

private void FillDataGridViewWithFilteredStudentAccordingToDisplayCheckBoxes()
{
    FilteredStudentDataGridView.Columns.Add("Id", "Numero");
    FilteredStudentDataGridView.Columns.Add("LastName", "Nom");
    FilteredStudentDataGridView.Columns.Add("FirstName", "Prenom");

    if (MiddleNameDisplayCheckBox.Checked)
    {
        FilteredStudentDataGridView.Columns.Add("MiddleName", "Nom Du Pere");
    }

    if (BirthdayDateDisplayCheckBox.Checked)
    {
        FilteredStudentDataGridView.Columns.Add("DateOfBirth", "Date De Naissance");
    }

    //Rest of the code omitted, but same concept.

    foreach (Student student in _filteredStudents)
    {
        List<object> rowsValues = new List<object>();
        foreach (object column in FilteredStudentDataGridView.Columns)
        {
            string columnName = ((DataGridViewTextBoxColumn)column).Name;

            if (columnName == "Id")
            {
                rowsValues.Add(student.StudentId);
            }

            if (columnName == "FirstName")
            {
                rowsValues.Add(student.FirstName);
            }
            //Code omitted.
        }
        object[] arrayRowsValues = rowsValues.ToArray();
        FilteredStudentDataGridView.Rows.Add(arrayRowsValues);
    }
}

我想知道是否有一种方法可以使用 LINQ 而不是所有那些 if block 来根据我的条件过滤数据?

最佳答案

我肯定会把过滤移到数据库中:

IQueryable<Student> studentQuery = context.Students;
if (ColonieFilterCheckBox.Checked) {
    studentQuery = studentQuery.Where(x => x.Colonie);
}

if (NatationFilterCheckBox.Checked) {
    studentQuery = studentQuery.Where(x => x.Nataion);
}

if (ExcursionFilterCheckBox.Checked) {
    studentQuery = studentQuery.Where(x => x.Excursion);
}

var _filteredStudents = await studentQuery.ToListAsync().ConfigureAwait(false);

如果你更喜欢本地过滤逻辑,你可以将条件结合起来,或者使用反射来简化代码但减慢一些。

对于组合条件,你可以这样做

_filteredStudents = _listOfAllStudents
    .Where(x => (!ColonieFilterCheckBox.Checked || x.Colonie) &&
                (!NatationFilterCheckBox.Checked || x.Natation) &&
                (!ExcursionFilterCheckBox.Checked || x.Excursion)).ToList();

但这会检查每个 StudentCheckBox

相反,您可以使用反射为过滤器动态构建代码(同样,假设您在字段后命名 CheckBox 控件):

IEnumerable<Student> queryStudents = _listOfAllStudents;
var xParm = Expression.Parameter(typeof(Student));

foreach (var filterField in new[] { "Colonie", "Natation", "Excursion" }) {
    if (((CheckBox)Controls.Find($"{filterField}CheckBox")).Checked) {
        var whereLambda = (Expression<Func<Student, bool>>)Expression.Lambda(Expression.PropertyOrField(xParm, filterField), xParm);
        queryStudents = queryStudents.Where(whereLambda.Compile());
    }
}
_filteredStudents = queryStudents.ToList();

对于您的显示逻辑,我会重命名所有复选框以匹配数据字段名称,然后使用大量反射:

private void FillDataGridViewWithFilteredStudentAccordingToDisplayCheckBoxes() {
    var headerText = new Dictionary<string, string> { { "Id", "Numero" }, { "LastName", "Nom" }, { "FirstName", "Prenom" }, { "MiddleName", "Nom Du Pere" },
    { "DateOfBirth", "Date De Naissance" } };

    var viewColumns = new List<string> { "Id", "LastName", "FirstName" };

    foreach (var possibleColumn in headerText.Keys) {
        var displayColumns = Controls.Find(possibleColumn + "DisplayCheckBox", true);
        if (displayColumns.Length == 1) {
            if (((CheckBox)displayColumns[0]).Checked)
                viewColumns.Add(possibleColumn);
        }
    }

    //Rest of the code omitted, but same concept.

    foreach (var dataFieldName in viewColumns)
        FilteredStudentDataGridView.Columns.Add(dataFieldName, headerText[dataFieldName]);

    foreach (var student in _filteredStudents) {
        var studentType = student.GetType();
        var rowValues = new List<object>();
        foreach (var dataFieldName in viewColumns)
            rowValues.Add(studentType.GetProperty(dataFieldName).GetValue(student, null));

        FilteredStudentDataGridView.Rows.Add(rowValues.ToArray());
    }
}

请注意,如果您关心显示的列的顺序,您将需要一些逻辑来对 headerText.Keys 进行排序或排序。在我的类似的 asp 实现中,我只有一个手动的过程调用列表,其中的数据名称按我想要的顺序排列,并且该过程检查是否要显示数据项(在 viewColumns 中) ,然后添加数据和列标题。

关于c# - 如何使用多个条件和 LINQ 过滤列表中的数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44553648/

相关文章:

c# - 了解异常处理

c# - 为什么我的 C# 程序在分析器中速度更快?

c# - 使用 MS 捆绑的空捆绑

c# - 如何删除由 c# 中的 xmlreader 打开的文件?

c# - 从另一个线程更新控件的属性 (label.Text)

c# - 是否有 LINQ 扩展或(一组合理/高效的 LINQ 扩展)确定集合是否至少有 'x' 个元素?

c# - 在 C# 中计算文件内容的哈希值?

c# - 数据未插入到 Access 数据库中

c# - 具有公共(public)字段的不同类之间的 IEnumerable.Except()

asp.net - 简单的 LINQ 和列表错误 : WhereListIterator`1[Task ]' to type ' System. Collections.Generic.List`1[Task]'