强类型绑定源过滤器

时间:2014-05-05 00:41:01

标签: c# .net winforms bindingsource

构建BindingSource.Filter字符串感觉就像丑陋手动强制过滤方式已经检索过的数据。

  1. 没有明确的类型检查
  2. 没有明确的列名检查
  3. 没有明确的SQL语法检查
  4. 需要手动ToString()格式化
  5. DataSet设计更改未传播到Filter
  6. 使用多个控件中的多个条件管理Filter很快就会变得单调乏味,容易出错并且难以处理。
  7. 使用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查询演示BindingSource DataSource(即:typedTableRow中可以访问.Where( ... )个字段。

    备注(for shriop):

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

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

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

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

4 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分:4)

好的,我认为这就是你想要的。我创建了一个名为AdventureWorks的类型化DataSet,并将Product表添加到其中。然后我将一个DataGridView和一个TextBox添加到一个Form。我在表单中添加了一个类型化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()方法。

虽然这是一种相当简单的方法,但它会在显示具有空值的列值时抛出异常,除非您进入DataSet设计器并更改处理空值的选项以返回null而不是抛出异常。这适用于字符串列,但对于其他列类型(如DateTime)则不太好。

在对DataView如何实现这一点进行大量研究之后,我发现了一个简单的全功能实现,并找到了最能描述疼痛的答案,Data binding dynamic data

如果您使用Simple way to convert datarow array to 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;
}

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

答案 2 :(得分:1)

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

我创建了一个用于过滤TypedTables的扩展程序,并从已退出的已填充BindingSource.Datasource的已过滤视图中填充我的typedTable,保留原始表并将模式保留在返回表中(返回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)

答案 3 :(得分:0)

我可以尝试提供这种方法的替代方法(将LINQ直接添加到Bindingsource过滤器中)。

对于用复杂的排序标准过滤数据,尤其是在数据与其他来源匹配的情况下,我一直不满意。我还需要保持tableadapter的更新功能正常工作,这似乎排除了一些麻烦。过去,我动态地创建了一个即席过滤器列,并使用了它,但是保持hte数据表笔直的工作相当繁琐,而且不是特别快也不是很漂亮。

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

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

创建一个绑定源扩展以获取列表并返回已编译的过滤器字符串是非常简单的事情。

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

关于绑定源过滤器的设施或限制的文献似乎很少,但是我看到有人使用 IN 带有很大字符串的报道。我个人不处理海量数据,因此这对我来说不是问题,但是也许,如果您认为此方法有用,那么您将想测试这些限制(当然还有性能)。

我希望这有助于激发人们的想象力-但是,如果您确实想将我击落,请-保持温柔:)