2017-06-14 76 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(); 
}