我创建此表单是为了生成一个学生列表,该列表能够按某些条件(左侧)进行过滤并显示所需的任何信息(右侧)
当表格开始初始化时,我用 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();
但这会检查每个 Student
的 CheckBox
。
相反,您可以使用反射为过滤器动态构建代码(同样,假设您在字段后命名 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/