C#使用LINQ过滤DataGridView时空值报错、逻辑反向问题求解
问题根因
- 空引用报错:你的LINQ查询用了多组左连接,关联表无匹配数据时对应字段值为null,DataGridView单元格
Value会是null或DBNull.Value,直接调用ToString()必然触发空引用异常。 - 过滤结果反向:现有代码逻辑是找到匹配筛选条件的行,把这些行设为隐藏,完全和需求相反;同时两个筛选条件的判断逻辑独立,第二个条件的
else分支会在未选择检验人筛选项时,把所有行重置为可见,直接覆盖前面类别的筛选结果。 - 隐藏bug:你代码中取检验人列用的列名是
inspector,但LINQ查询输出的匿名类中对应字段名是inspected_by,本身就会找不到列报错。
修复方案
你可以任选以下一种方案实现,第二种数据源过滤方案稳定性和性能更优。
方案1:修正原有行可见性控制逻辑
直接修复你现有代码的逻辑问题,处理空值、修正判断方向、补全多条件协同逻辑:
// 先获取选中的筛选值 string selectCat = cbx_category.Text.Trim(); string selectInsp = cbx_inspector.Text.Trim(); // 遍历所有非新行判断可见性 foreach (DataGridViewRow row in dtg_Data.Rows.OfType<DataGridViewRow>()) { if (row.IsNewRow) continue; // 类别筛选判断,默认匹配 bool catPass = true; if (selectCat != "Category") { object catVal = row.Cells["category"].Value; string catText = catVal == null || catVal == DBNull.Value ? string.Empty : catVal.ToString().Trim(); catPass = catText == selectCat; } // 检验人筛选判断,默认匹配 bool inspPass = true; if (selectInsp != "Inspector") { // 注意列名是inspected_by,不是inspector object inspVal = row.Cells["inspected_by"].Value; string inspText = inspVal == null || inspVal == DBNull.Value ? string.Empty : inspVal.ToString().Trim(); inspPass = inspText == selectInsp; } // 两个筛选条件都通过才显示 row.Visible = catPass && inspPass; }
方案2:数据源级过滤(推荐)
由于你是通过DataSource绑定数据,直接在内存中过滤全量数据再重新绑定,比操作DataGridView行状态更稳定,不会出现控件行状态相关的异常。
- 首先在窗体类中定义全局变量,存储查询得到的全量数据:
// 存储全量未过滤数据,因为是匿名类先存为dynamic列表,后续自定义强类型模型替换更稳妥 private List<dynamic> _fullDataSource;
- 修改你原来的数据加载代码,将查询结果存入全局变量再绑定:
var result = (from t1 in db.tbl_item_lists // 保留你原来的所有左连接逻辑,完全不用改 join t2 in db.tbl_uoms on t1.uom equals t2.uom_id into t12 from t121 in t12.DefaultIfEmpty() join t3 in db.tbl_mode_procs on t1.mode_of_procurement equals t3.mop_id into t13 from t131 in t13.DefaultIfEmpty() join t4 in db.tbl_inspectors on t1.inspected_by equals t4.inspector_id into t14 from t141 in t14.DefaultIfEmpty() join t5 in db.tbl_representatives on t1.ofm_representative equals t5.representative_id into t15 from t151 in t15.DefaultIfEmpty() join t6 in db.tbl_receivers on t1.received_by equals t6.receiver_id into t16 from t161 in t16.DefaultIfEmpty() join t7 in db.tbl_status on t1.status equals t7.status_id into t17 from t171 in t17.DefaultIfEmpty() join t8 in db.tbl_categories on t1.category equals t8.category_id into t18 from t181 in t18.DefaultIfEmpty() select new { barcode = t1.barcode, item_name = t1.item_name, description = t1.description, date_procured = t1.date_procured, category = t181.category, part_number = t1.part_number, serial_number = t1.serial_number, batch_number = t1.batch_number, last_borrower = t1.last_borrower, purpose = t1.purpose, date_returned = t1.date_returned, uom = t121.uom, quantity = t1.quantity, mode_of_procurement = t131.mode_proc, price = t1.price, inspected_by = t141.full_name, ofm_representative = t151.full_name, received_by = t161.full_name, contract = t1.contract, proponent = t1.proponent, status = t171.status_desc, delivery_date = t1.delivery_date }).ToList(); // 存入全局变量 _fullDataSource = result.Cast<dynamic>().ToList(); dtg_Data.DataSource = _fullDataSource;
- 编写统一的过滤方法,在两个ComboBox的
SelectedIndexChanged事件中调用即可:
private void ApplyGridFilter() { string selectCat = cbx_category.Text.Trim(); string selectInsp = cbx_inspector.Text.Trim(); IEnumerable<dynamic> filtered = _fullDataSource; if (selectCat != "Category") { filtered = filtered.Where(r => r.category != null && r.category.ToString().Trim() == selectCat); } if (selectInsp != "Inspector") { filtered = filtered.Where(r => r.inspected_by != null && r.inspected_by.ToString().Trim() == selectInsp); } dtg_Data.DataSource = filtered.ToList(); }
提示:如果后续需要扩展更多筛选条件,直接在
ApplyGridFilter方法中追加判断逻辑即可,维护成本远低于操作DataGridView行的方案。
内容的提问来源于stack exchange,提问作者Raibyfe
相关产品推荐
相关产品推荐

