2017-06-14 96 views
0

我创建这个表格来生成学生的列表,包括由一些标准筛选(在左侧)和显示所需的任何信息(从右侧)
List generation Form如何使用多个条件和LINQ过滤列表中的数据?

的能力当窗体在初始化开始我抓住了整个学生名单与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块根据我的条件筛选数据?

+1

假设'x.Colonie'和其他的是bools这可以工作'Where(x =>(x.Colonie && ColonieFilterCheckBox.Checked)&&/* other conditions * /)'等 – JSteward

+1

请注意'.Select x => x)'是一个空操作,不需要。此外,不应该需要内部的'.ToList()',因为'Intersect'带有'IEnumerable'。真的,你需要的只是'_filteredStudents = _filteredStudents.Where(x => x.Excursion).ToList();'。 – NetMage

+0

@JSteward不是所有的都是'bool',但是我可以通过检查'!string.IsNullOrWhiteSpace'来做到这一点,因为我不让它们插入空字符串的数据。 –

回答

2

我会过滤绝对移动到数据库:

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)); 
var xParmArray = new[] { xParm }; 

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), xParmArray); 
     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中),然后添加数据和列头。

+0

我不想将所有过滤器移动到数据库,因为在一个会话中,用户可能会生成超过20个列表,所以仅仅过滤我认为的列表会更快。关于反思,它看起来很有希望,并会尝试并回复你。谢谢。 –

+0

检索'Students'来创建本地查询后,可以使用类似的逻辑。我认为速度取决于检索多少学生记录与数据库速度/延迟。 – NetMage

+0

@PaulKaram我无法抗拒使用更多反射来添加一些本地选项。 – NetMage

1

你可以在一个方法中重用你的linq表达式。尝试使用AsQueryable扩展方法从System.Linq命名空间来代替:

private ICollection<Student> FilterStudents(ICollection<Student> students) 
{ 
    var query = students.AsQueryable(); 

    if (ColonieFilterCheckBox.Checked) 
    { 
     query = query.Where(x=>x.Colonie); 
    } 

    if (NatationFilterCheckBox.Checked) 
    { 
     query = query.Where(x=>x.Nation); 
    } 

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

    return query.ToList(); 
}