繁体   English   中英

强类型绑定源过滤器

[英]Strongly-Typed BindingSource Filter

构造一个BindingSource.Filter字符串感觉像是一种丑陋的手动的强制的过滤已检索数据的方式。

  1. 没有明确的类型检查
  2. 没有明确的列名检查
  3. 没有显式的 SQL 语法检查
  4. 需要手动ToString()格式
  5. DataSet设计更改未传播到Filter
  6. 使用来自多个控件的多个条件来管理Filter很快就会变得乏味、容易出错且笨拙。

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

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

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

额外赏金:
所有通过IListDataViewDataRowViewDataRowtypedTableRow都被证明是相当混乱的。

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

使用强类型 LINQ 查询(即:在.Where( ... )可访问typedTableRow字段.Where( ... )演示带有DataSourceBindingSource

注意事项(虾):

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

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

myBindingSource.DataSource 
    = typedDataSet.typedTable.Where( x => x.table_id > 3).ToList();

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

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

this.BindingSource.DataSource = ((IList<T>)this.BindingSource.List).Where( ... );

好的,我想这就是你想要的。 我创建了一个名为 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();

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

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

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

您还必须记住在应用过滤条件时注意可空字段,并确保使用类型化的 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;
}

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

我在vb.net做了同样的事情并在这里分享它可能对某人有用:

我创建了一个扩展,用于过滤TypedTables并从退出填充的typedTable的过滤视图填充我的BindingSource.Datasource ,保留原始表并将架构保留在返回表中(返回typedTable而不是 DataTable):

Imports System.Runtime.CompilerServices
Imports System.Windows.Forms

Public Module DB_Extensions

    <Extension()> _
    Public Function LINQ_Filter(Of RowT As DataRow)(ByRef table As TypedTableBase(Of RowT), predicate As System.Func(Of RowT, Boolean)) As TypedTableBase(Of RowT)
        ' Create a clone of table structure(without data) to not loose filtered data on original table and return in a new table instead
        Dim ret As TypedTableBase(Of RowT) = table.Clone()
        ' Using .ImportRow() method garantees to not loose original table schema after return
        For Each r As RowT In table.Where(predicate)
            ret.ImportRow(r)
        Next
        Return ret
    End Function

End Module

并以这种方式简单地使用它(需要导入DB_Extensions模块才能工作):

myBindingSource.DataSource = someTypedTable.LINQ_Filter(your_filters)

我可以暂时提供这种方法的替代方法(将 LINQ 直接插入到 Bindingsource 过滤器中)。

我已经解决了这个问题,用于过滤具有复杂排序标准的数据,尤其是在数据与其他来源匹配的情况下。 我还需要保持 tableadapter 的更新功能正常工作,这似乎排除了一些策略。 在过去,我动态创建了一个临时过滤器列并使用它,但是保持 hte 数据表直的维护相当乏味,而且它不是特别快或漂亮。

最近我意识到 bindingsource 过滤器对 SQL 的IN命令有一个类似的选项。

一个例子(对不起它的 VB)是bindingsource.filter = "[columnname] IN ('n','n','n','n')"等,其中每个 N 是要匹配的值列表之一。

创建一个 bindingsource 扩展来获取一个列表并返回编译后的过滤器字符串是一件非常简单的事情。

通过使用此方法,您可以使用 Linq(或任何其他方法)来创建包含列表。 就我而言,我使用了要包含在列表内容中的记录的唯一 ID 键(通常为整数)。

关于 bindingsource 过滤器的工具或限制的文档似乎很少,但是我看到有人使用IN和非常大的字符串的报告。 我个人不处理大量数据,所以这对我来说不是问题,但也许,如果你认为这种方法有用,你会想要测试这些限制(当然还有性能)。

我希望这有助于激发某人的想象力 - 但如果你真的想打倒我 - 请 - 温柔:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM