强类型绑定源过滤器

Ste*_*ven 4 .net c# bindingsource winforms

构建BindingSource.Filter感觉等的丑陋,手册,被迫滤波已经检索到的数据的方式.

  1. 没有明确的类型检查
  2. 没有明确的列名检查
  3. 没有明确的SQL语法检查
  4. 需要手动ToString()格式化
  5. DataSet 设计变更未传播到 Filter
  6. Filter从多个控件管理多个条件很快就会变得乏味,容易出错并且难以处理.

使用a typedTableAdapter.FillBy(typedDataSet.typedTable, @params ...)是一种强大,简单,直接的方法,用于在数据库和数据库之间进行"过滤" DataSet.

.NET是否在强类型控件DataSetForm控件之间提供任何强类型过滤(可能通过a BindingSource)?

初始赏金:
最初的赏金是为概念证明而授予的(使用LINQ查询作为DataSource).但是,它没有演示如何实际访问强类型typedTableRow以执行过滤.

额外奖励:
所有铸件通过IList,DataView,DataRowView,DataRow,和typedTableRow已被证明是相当混乱.

Object                         Generic       Watch List Type
------                         --------      ------------
myBindingSource.List           IList         {System.Data.DataView}
myBindingSource.List[0]        object        {System.Data.DataRowView}

((DataRowView)myBindingSource.List[0]).Row
                               DataRow       typedTableRow
Run Code Online (Sandbox Code Playgroud)

表现出BindingSourceDataSource使用强类型的LINQ查询(即:与typedTableRow字段访问.Where( ... )).

笔记(为shriop):

  • 表单控件必须绑定myBindingSource.
  • myBindingSource.DataSource: typedDataSet
  • myBindingSource.DataMember: typedTable

过滤代码(applied in FilterBtn_Click()):

myBindingSource.DataSource 
    = typedDataSet.typedTable.Where( x => x.table_id > 3).ToList();
Run Code Online (Sandbox Code Playgroud)

过滤后,BindingNavigator显示相应的记录数.但是,如果我导航到任何包含空值的记录,我会StrongTypingException被抛入typedDataSet.typedTableRow.get_FIELDNAME().由于此行为仅在过滤后发生,因此我假设LINQ过滤会破坏数据绑定中的某些内容.

Mau*_*tro 5

您可以在绑定源上使用linq:

this.BindingSource.DataSource = ((IList<T>)this.BindingSource.List).Where( ... );
Run Code Online (Sandbox Code Playgroud)


小智 5

好的,我想这就是你想要的。我创建了一个名为 AdventureWorks 的类型化数据集,并向其中添加了 Product 表。然后我将一个 DataGridView 和一个 TextBox 添加到一个表单中。我在表单中添加了一个类型化 DataSet 的实例。我在表单中添加了一个 BindingSource。我将 BindingSource 的 DataSource 设置为表单上的 DataSet 实例。我将 DataMember 设置为 Product 表,它在表单上生成了一个 ProductTableAdapter。我将 DataGridView 的 DataSource 设置为 BindingSource。我将 TextBox 的 Text 属性绑定到 BindingSource 的 Name 属性,后者解析为 Product 表的 Name 列。在 OnLoad 中,它已经使用 TableAdapter 和 Product DataTable 为我生成了一个 Fill。然后我只需要添加一行来设置我的输入过滤器:

this.bindingSource.DataSource = this.adventureWorks.Product.Where(p => !p.IsProductSubcategoryIDNull()).ToList();
Run Code Online (Sandbox Code Playgroud)

然后我运行表单,只能看到过滤后的行集,当我点击它们时,TextBox 的文本将更改以匹配所选行的产品名称。

ToList 是关键,因为 BindingSource 在绑定或向绑定控件提供值时会做一些愚蠢的事情,如果没有它,您将收到一个异常,表示

The method or operation is not implemented.
   at System.Linq.Enumerable.Iterator`1.System.Collections.IEnumerator.Reset()
   ...
Run Code Online (Sandbox Code Playgroud)

您还必须记住在应用过滤条件时注意可空字段,并确保您使用的是类型化的 Is*Null() 方法。

虽然这是一种相当简单的方法,但它在显示具有空值的列值时会引发异常,除非您进入数据集设计器并将处理空值的选项更改为返回空值而不是引发异常。这适用于字符串列,但不适用于 DateTime 等其他列类型。

在对 DataView 如何实现这一点进行了大量研究后,DataTable 在内部使用了它,我找不到一个简单的全功能实现,但确实找到了最能描述痛苦的答案Data binding dynamic data

如果您可以绑定到数据的副本,我确实找到了一个非常简单的解决方案,使用一些逻辑来自 Simple way 中的将 datarow array 转换为 datatable。这使您返回到 DataTable,并使用它的实现,但过滤后只有您的行。

DataRow[] rows = this.adventureWorks.Product.Where(p => !p.IsProductSubcategoryIDNull()).ToArray();
if (rows.Length > 0)
{
    this.bindingSource.DataSource = rows.CopyToDataTable();
}
else
{
    this.bindingSource.DataSource = rows;
}
Run Code Online (Sandbox Code Playgroud)

现在,如果您从 DataSource 中取出 DataTable,您仍然应该能够使用此数据副本将更新发送回数据库,确保它是 DataTable 而不是 DataRow[],并将该 DataTable 发送到TableAdapter 的 Update 方法。然后,根据您的处理方式,您可以重新填充原始表,并重新应用过滤器。