Filtro con DataView (LINQ to DataSet)

La possibilità di filtrare i dati in base a criteri specifici e quindi di presentarli a un client tramite un controllo dell'interfaccia utente rappresenta un aspetto importante dell'associazione dati. Con DataView è possibile filtrare i dati e restituire subset di righe di dati che soddisfano criteri specifici di filtro. Oltre alle funzionalità di filtro basate su stringa, DataView offre anche la possibilità di usare espressioni LINQ per i criteri di filtro. Le espressioni LINQ consentono operazioni di filtro molto più complesse e potenti rispetto al filtro basato su stringhe.

Per filtrare i dati tramite DataView, sono disponibili due modalità:

  • Creare un oggetto DataView da una query LINQ to DataSet con una clausola Where.

  • Usare le funzionalità di filtro basate su stringa esistenti di DataView.

Creazione di DataView da una query con informazioni di filtro

È possibile creare un oggetto DataView da una query LINQ to DataSet. Se la query contiene una clausola Where, l'oggetto DataView viene creato con le informazioni di filtro della query. L'espressione nella clausola Where viene usata per determinare le righe di dati da includere in DataView e rappresenta la base per il filtro.

I filtri basati su espressione sono più potenti e complessi rispetto a quelli più semplici basati su stringa. I filtri basati su stringa e quelli basati su espressione si escludono a vicenda. Se si imposta un oggetto RowFilter basato su stringa dopo la creazione di un oggetto DataView da una query, il filtro basato su espressione inferito dalla query viene cancellato.

Nota

Nella maggior parte dei casi le espressioni usate per il filtro non devono presentare effetti collaterali e devono essere deterministiche. Le espressioni non devono inoltre contenere eventuale codice che dipende da un numero impostato di esecuzioni perché è possibile che le operazioni di filtro vengano eseguite un numero qualsiasi di volte.

Esempio

Nell'esempio seguente viene eseguita una query sulla tabella SalesOrderDetail per individuare gli ordini con una quantità maggiore di 2 e minore di 6. Viene quindi creato un oggetto DataView dalla query e l'oggetto DataView viene infine associato a BindingSource.

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

Esempio

Nell'esempio seguente viene creato un oggetto DataView da una query eseguita per individuare gli ordini effettuati dopo il 6 giugno 2001:

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

Esempio

Il filtro può essere anche combinato anche con l'ordinamento. Nell'esempio seguente viene creato un oggetto DataView da una query eseguita per individuare i contatti il cui cognome inizia con "S", quindi i risultati vengono ordinati in base a cognome e poi nome:

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()

Esempio

Nell'esempio seguente viene usato l'algoritmo SoundEx per individuare i contatti il cui cognome è simile a "Zhu". L'algoritmo SoundEx è implementato nel metodo 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 è un algoritmo fonetico, originariamente sviluppato dal Census Bureau USA, che viene utilizzato per indicizzare i nomi in base al suono, così come vengono pronunciati in inglese. Il metodo SoundEx restituisce un codice a quattro caratteri per un nome, costituito da una lettera seguita da tre numeri. La lettera corrisponde all'iniziale, mentre i numeri codificano le altre consonanti del nome. I nomi pronunciati in modo simile condividono lo stesso codice SoundEx. Di seguito è illustrata l'implementazione SoundEx usata nel metodo SoundEx dell'esempio precedente:

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

Utilizzo della proprietà RowFilter

La funzionalità di filtro basata su stringa esistente di DataView è comunque utilizzabile nel contesto LINQ to DataSet. Per altre informazioni sul filtro RowFilter basato su stringhe, vedere Ordinamento e filtro dei dati.

Nell'esempio seguente viene creato un oggetto DataView dalla tabella Contact e viene quindi impostata la proprietà RowFilter per restituire le righe in cui il cognome del contatto è "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()

Dopo la creazione di un oggetto DataView da una query DataTable o LINQ to DataSet, è possibile utilizzare la proprietà RowFilter per specificare subset di righe in base ai relativi valori di colonna. I filtri basati su stringa e quelli basati su espressione si escludono a vicenda. L'impostazione della proprietà RowFilter cancella l'espressione filtro dedotta dalla query LINQ to DataSet e l'espressione di filtro non può essere reimpostata.

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'"

Per restituire i risultati di una particolare query sui dati anziché fornire una visualizzazione dinamica di un subset di dati, è possibile usare i metodi Find o FindRows di DataView anziché impostare la proprietà RowFilter. L'utilizzo della proprietà RowFilter è consigliato in applicazioni con associazioni a dati, in cui i risultati filtrati vengono visualizzati da un controllo associato. L'impostazione della proprietà RowFilter provoca una ricompilazione dell'indice dei dati, aggiungendo un sovraccarico all'applicazione e riducendo le prestazioni. I metodi Find e FindRows si avvalgono dell'indice corrente, senza che sia necessario ricompilarlo. Se si intende chiamare Find o FindRows solo una volta, è opportuno usare l'oggetto DataView esistente. Se invece si intende chiamare Find o FindRows più volte, è opportuno creare un nuovo oggetto DataView per ricompilare l'indice sulla colonna da usare per la ricerca e quindi chiamare il metodo Find o FindRows. Per altre informazioni sui metodi Find e FindRows, vedere Ricerca di righe e prestazioni di DataView.

Cancellazione del filtro

È possibile cancellare il filtro su un oggetto DataView, impostato tramite la proprietà RowFilter. Per cancellare un filtro su DataView, sono disponibili due modalità:

  • Impostare la proprietà RowFilter su null.

  • Impostare la proprietà RowFilter su una stringa vuota.

Esempio

Nell'esempio seguente viene creato un oggetto DataView da una query, quindi il filtro viene cancellato impostando la proprietà RowFilter su 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

Esempio

Nell'esempio seguente viene creato un oggetto DataView da una tabella e viene impostata la proprietà RowFilter. Il filtro viene quindi cancellato impostando la proprietà RowFilter su una stringa vuota:

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 = ""

Vedi anche