DataView によるフィルター処理 (LINQ to DataSet)

特定の条件に基づいてデータをフィルター処理し、UI コントロールを介してそのデータをクライアントに提供する機能は、データ バインドの重要な特徴です。 DataView は、データにフィルターを適用し、特定のフィルター条件を満たすデータ行のサブセットを返す方法をいくつか提供します。 文字列ベースのフィルター処理機能に加え、DataView には、フィルター条件として LINQ 式を使用する機能も用意されています。 LINQ 式を使用すると、文字列ベースのフィルター処理よりはるかに複雑で強力なフィルター処理を実現できます。

DataView を使用してデータをフィルター処理する方法は 2 つあります。

  • Where 句を含む LINQ to DataSet クエリから DataView を作成します。

  • DataView の既存の文字列ベースのフィルター機能を使用します。

フィルター情報を含むクエリによる DataView の作成

DataView オブジェクトは LINQ to DataSet クエリから作成できます。 このクエリに Where 句が含まれている場合、DataView はクエリのフィルター情報を使用して作成されます。 Where 句内の式は、DataView に含めるデータ行の決定に使用され、これがフィルターの基礎となります。

式ベースのフィルターは、文字列ベースのフィルターよりもはるかに強力で複雑なフィルター機能を提供します。 文字列ベースのフィルターと式ベースのフィルターは、相互に排他的です。 RowFilter をクエリから作成した後に文字列ベースの DataView を設定した場合、クエリから推論される式ベースのフィルターはクリアされます。

Note

ほとんどの場合、フィルターに使用する式は、副作用のない確定的な式である必要があります。 また、フィルター処理は任意の回数実行されるため、特定の実行回数に依存するロジックが式に含まれないようにしてください。

次の例では、SalesOrderDetail テーブルに対して数量が 3 個以上 5 個以下の注文を取得するクエリを実行し、このクエリから DataView を作成します。次に、DataViewBindingSource にバインドします。

DataTable orders = _dataSet.Tables["SalesOrderDetail"];

EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()
                                         where order.Field<short>("OrderQty") > 2 && order.Field<short>("OrderQty") < 6
                                         select order;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;
Dim orders As DataTable = dataSet.Tables("SalesOrderDetail")

Dim query = _
    From order In orders.AsEnumerable() _
    Where order.Field(Of Int16)("OrderQty") > 2 And _
          order.Field(Of Int16)("OrderQty") < 6 _
    Select order

Dim view As DataView = query.AsDataView()
bindingSource1.DataSource = view

次の例では、2001 年 6 月 6 日以降に受けた注文に対するクエリから DataView を作成します。

DataTable orders = _dataSet.Tables["SalesOrderHeader"];

EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()
                                         where order.Field<DateTime>("OrderDate") > new DateTime(2002, 6, 1)
                                         select order;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;
Dim orders As DataTable = dataSet.Tables("SalesOrderHeader")

Dim query = _
    From order In orders.AsEnumerable() _
    Where order.Field(Of DateTime)("OrderDate") > New DateTime(2002, 6, 1) _
    Select order

Dim view As DataView = query.AsDataView()
bindingSource1.DataSource = view

フィルターは並べ替えと組み合わせることもできます。 次の例では、姓が "S" で始まる連絡先を姓、名の順に並べ替えるクエリから DataView を作成します。

DataTable contacts = _dataSet.Tables["Contact"];

EnumerableRowCollection<DataRow> query = from contact in contacts.AsEnumerable()
                                         where contact.Field<string>("LastName").StartsWith("S")
                                         orderby contact.Field<string>("LastName"), contact.Field<string>("FirstName")
                                         select contact;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();

Dim contacts As DataTable = dataSet.Tables("Contact")

Dim query = _
    From contact In contacts.AsEnumerable() _
    Where contact.Field(Of String)("LastName").StartsWith("S") _
    Order By contact.Field(Of String)("LastName"), contact.Field(Of String)("FirstName") _
    Select contact

Dim view As DataView = query.AsDataView()
bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()

次の例では、SoundEx アルゴリズムを使用して、"Zhu" に類似する姓を持つ連絡先を検索します。 SoundEx アルゴリズムは、SoundEx メソッドに実装されています。

DataTable contacts = _dataSet.Tables["Contact"];

var soundExCode = SoundEx("Zhu");

EnumerableRowCollection<DataRow> query = from contact in contacts.AsEnumerable()
                                         where SoundEx(contact.Field<string>("LastName")) == soundExCode
                                         select contact;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();
Dim contacts As DataTable = dataSet.Tables("Contact")
Dim soundExCode As String = SoundEx("Zhu")

Dim query = _
    From contact In contacts.AsEnumerable() _
    Where SoundEx(contact.Field(Of String)("LastName")) = soundExCode _
    Select contact

Dim view As DataView = query.AsDataView()
bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()

SoundEx は、米国国勢調査局 (U.S. Census Bureau) で最初に開発された、名前を英語の発音でインデックス化する音声アルゴリズム国勢調査局から無料で入手できます。 SoundEx メソッドは、1 つの名前に対して、1 つの英字とそれに続く 3 つの数字から構成される 4 文字のコードを返します。 文字は名前の最初の文字であり、数字は名前に含まれる残りの子音をコード化したものです。 発音が近い名前には、同じ SoundEx コードが与えられます。 前の例の SoundEx メソッドに使用されている SoundEx 実装を次に示します。

static string SoundEx(string word)
{
    // The length of the returned code.
    const int length = 4;

    // Value to return.
    var value = "";

    // The size of the word to process.
    var size = word.Length;

    // The word must be at least two characters in length.
    if (size > 1)
    {
        // Convert the word to uppercase characters.
        word = word.ToUpper(CultureInfo.InvariantCulture);

        // Convert the word to a character array.
        var chars = word.ToCharArray();

        // Buffer to hold the character codes.
        var buffer = new StringBuilder
        {
            Length = 0
        };

        // The current and previous character codes.
        var prevCode = 0;
        var currCode = 0;

        // Add the first character to the buffer.
        buffer.Append(chars[0]);

        // Loop through all the characters and convert them to the proper character code.
        for (var i = 1; i < size; i++)
        {
            switch (chars[i])
            {
                case 'A':
                case 'E':
                case 'I':
                case 'O':
                case 'U':
                case 'H':
                case 'W':
                case 'Y':
                    currCode = 0;
                    break;
                case 'B':
                case 'F':
                case 'P':
                case 'V':
                    currCode = 1;
                    break;
                case 'C':
                case 'G':
                case 'J':
                case 'K':
                case 'Q':
                case 'S':
                case 'X':
                case 'Z':
                    currCode = 2;
                    break;
                case 'D':
                case 'T':
                    currCode = 3;
                    break;
                case 'L':
                    currCode = 4;
                    break;
                case 'M':
                case 'N':
                    currCode = 5;
                    break;
                case 'R':
                    currCode = 6;
                    break;
            }

            // Check if the current code is the same as the previous code.
            if (currCode != prevCode)
            {
                // Check to see if the current code is 0 (a vowel); do not process vowels.
                if (currCode != 0)
                {
                    buffer.Append(currCode);
                }
            }
            // Set the previous character code.
            prevCode = currCode;

            // If the buffer size meets the length limit, exit the loop.
            if (buffer.Length == length)
            {
                break;
            }
        }
        // Pad the buffer, if required.
        size = buffer.Length;
        if (size < length)
        {
            buffer.Append('0', length - size);
        }

        // Set the value to return.
        value = buffer.ToString();
    }
    // Return the value.
    return value;
}
Private Function SoundEx(ByVal word As String) As String

    Dim length As Integer = 4
    ' Value to return
    Dim value As String = ""
    ' Size of the word to process
    Dim size As Integer = word.Length
    ' Make sure the word is at least two characters in length
    If (size > 1) Then
        ' Convert the word to all uppercase
        word = word.ToUpper(System.Globalization.CultureInfo.InvariantCulture)
        ' Convert the word to character array for faster processing
        Dim chars As Char() = word.ToCharArray()
        ' Buffer to build up with character codes
        Dim buffer As StringBuilder = New StringBuilder()
        ' The current and previous character codes
        Dim prevCode As Integer = 0
        Dim currCode As Integer = 0
        ' Append the first character to the buffer
        buffer.Append(chars(0))
        ' Loop through all the characters and convert them to the proper character code
        For i As Integer = 1 To size - 1
            Select Case chars(i)

                Case "A", "E", "I", "O", "U", "H", "W", "Y"
                    currCode = 0

                Case "B", "F", "P", "V"
                    currCode = 1

                Case "C", "G", "J", "K", "Q", "S", "X", "Z"
                    currCode = 2

                Case "D", "T"
                    currCode = 3

                Case "L"
                    currCode = 4

                Case "M", "N"
                    currCode = 5

                Case "R"
                    currCode = 6
            End Select

            ' Check to see if the current code is the same as the last one
            If (currCode <> prevCode) Then

                ' Check to see if the current code is 0 (a vowel); do not process vowels
                If (currCode <> 0) Then
                    buffer.Append(currCode)
                End If
            End If
            ' Set the new previous character code
            prevCode = currCode
            ' If the buffer size meets the length limit, then exit the loop
            If (buffer.Length = length) Then
                Exit For
            End If
        Next
        ' Pad the buffer, if required
        size = buffer.Length
        If (size < length) Then
            buffer.Append("0", (length - size))
        End If
        ' Set the value to return
        value = buffer.ToString()
    End If
    ' Return the value
    Return value
End Function

RowFilter プロパティの使用

DataView の既存の文字列ベースのフィルター機能は、LINQ to DataSet のコンテキストでそのまま動作します。 文字列ベースの RowFilter のフィルター処理について詳しくは、「データの並べ替えとフィルター処理」をご覧ください。

次の例では、Contact テーブルから DataView を作成し、RowFilter プロパティを設定して、連絡先の姓が "Zhu" である行を返します。

DataTable contacts = _dataSet.Tables["Contact"];

DataView view = contacts.AsDataView();

view.RowFilter = "LastName='Zhu'";

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();
Dim contacts As DataTable = dataSet.Tables("Contact")

Dim view As DataView = contacts.AsDataView()
view.RowFilter = "LastName='Zhu'"
bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()

DataView または LINQ to DataSet クエリから DataTable を作成した後は、RowFilter プロパティを使用して、列の値に基づいて行のサブセットを指定できます。 文字列ベースのフィルターと式ベースのフィルターは、相互に排他的です。 RowFilter プロパティを設定すると、LINQ to DataSet クエリから推論されたフィルター式はクリアされ、フィルター式を再設定することはできません。

DataTable contacts = _dataSet.Tables["Contact"];

EnumerableRowCollection<DataRow> query = from contact in contacts.AsEnumerable()
                                         where contact.Field<string>("LastName") == "Hernandez"
                                         select contact;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();

view.RowFilter = "LastName='Zhu'";
Dim contacts As DataTable = dataSet.Tables("Contact")

Dim query = _
    From contact In contacts.AsEnumerable() _
    Where contact.Field(Of String)("LastName") = "Hernandez" _
    Select contact

Dim view As DataView = query.AsDataView()
bindingSource1.DataSource = view

dataGridView1.AutoResizeColumns()
view.RowFilter = "LastName='Zhu'"

データ サブセットの動的ビューの作成とは対照的に、データに対する特定のクエリの結果を取得する場合は、Find プロパティを設定する代わりに FindRowsDataView メソッドまたは RowFilter メソッドを使用します。 RowFilter プロパティは、データ連結アプリケーションでの使用に適しています。このアプリケーションでは、連結されたコントロールによってフィルター処理結果が表示されます。 RowFilter プロパティを設定すると、データのインデックスが再構築され、アプリケーションのオーバーヘッドが増加してパフォーマンスの低下を招きます。 Find メソッドと FindRows メソッドでは、現在のインデックスが使用されます。このため、インデックスを再構築する必要はありません。 Find または FindRows を 1 回だけ呼び出す場合は、既存の DataView を使用するようにします。 Find または FindRows を複数回呼び出す場合は、新しい DataView を作成して検索対象の列のインデックスを再構築した後、Find メソッドまたは FindRows メソッドを呼び出します。 Find メソッドと FindRows メソッドについて詳しくは、「行の検索」および「DataView のパフォーマンス」をご覧ください。

フィルターのクリア

DataView のフィルターは、RowFilter プロパティを使用すると、フィルタリングが設定された後にクリアできます。 DataView のフィルターは、2 つの異なる方法でクリアできます。

  • RowFilter プロパティを nullに設定します。

  • RowFilter プロパティを空の文字列に設定します。

次の例では、クエリから DataView を作成した後、RowFilter プロパティを null に設定してフィルターをクリアします。

DataTable orders = _dataSet.Tables["SalesOrderHeader"];

EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()
                                         where order.Field<DateTime>("OrderDate") > new DateTime(2002, 11, 20)
                                            && order.Field<decimal>("TotalDue") < new decimal(60.00)
                                         select order;

DataView view = query.AsDataView();

bindingSource1.DataSource = view;

view.RowFilter = null;
Dim orders As DataTable = dataSet.Tables("SalesOrderHeader")

Dim query = _
    From order In orders.AsEnumerable() _
    Where order.Field(Of DateTime)("OrderDate") > New DateTime(2002, 11, 20) _
        And order.Field(Of Decimal)("TotalDue") < New Decimal(60.0) _
    Select order

Dim view As DataView = query.AsDataView()
bindingSource1.DataSource = view
view.RowFilter = Nothing

次の例では、テーブルから DataView を作成し、RowFilter プロパティを設定します。その後、RowFilter プロパティを空の文字列に設定してフィルターをクリアします。

DataTable contacts = _dataSet.Tables["Contact"];

DataView view = contacts.AsDataView();

view.RowFilter = "LastName='Zhu'";

bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();

// Clear the row filter.
view.RowFilter = "";
Dim contacts As DataTable = dataSet.Tables("Contact")

Dim view As DataView = contacts.AsDataView()
view.RowFilter = "LastName='Zhu'"
bindingSource1.DataSource = view
dataGridView1.AutoResizeColumns()

' Clear the row filter.
view.RowFilter = ""

関連項目