En el anterior post Linq To DataSet, contamos las bondades que nos ofrece este proveedor de Linq para la búsqueda y ordenación de filas en un objeto DataTable. Sin embargo, me gustaría enfrentar las posibilidades actuales de filtrado y ordenación de las que disponemos actualmente en un objeto DataTable contra las nuevas y mejoradas opciones que nos ofrece Linq To DataSet.
Siendo así, haremos un breve repaso sobre las propiedades y métodos más relevantes que, de forma nativa, ofrece un DataTable para la búsqueda, filtrado y ordenación de filas, para finalmente enfrentarlos a sus equivalentes en Linq To DataSet, y así convencernos plenamente de la potencia y superioridad que ofrece el proveedor de Linq para DataSet.
En un objeto DataTable, la única operación de búsqueda de filas disponible es el método Select.
DataTable.Select(filterExpression[, sort])
El método Select devuelve un array de objetos DataRow que cumple el filtro especificado y, opcionalmente, ordenado según el criterio de ordenación especificado.
Existe otra sobrecarga de Select que permite devolver filas a partir de un criterio de búsqueda y según el estado de las filas a partir del enumerado DataViewRowState.
La sintaxis para la expresión de filtro y los operadores disponibles puedes encontrarla aquí.
Cualquier objeto DataTable tiene automáticamente disponible un objeto DataView asociado en la propiedad DataTable.DefaultView. En cualquier caso, somos libres de crear tantos objetos DataView asociados a un DataTable como creamos oportuno.
Un objeto DataView es simplemente una lista de punteros a objetos DataRow del DataTable asociado, con un filtro y ordenación específico. Buscando una equivalencia, serían como las vistas en una base de datos.
De este modo, al poder tener distintos objetos DataView asociados al mismo DataTable lo que conseguimos es poder tener distintos grupos de filas ordenadas y filtradas por diferentes criterios.
Como hemos dicho antes, bien podemos trabajar sobre el objeto DataView por defecto (DataTable.DefaultView) o bien crear un nuevo objeto DataView.
Para crear un nuevo objeto DataView:
Dim miDataView As New DataView(miDataTable)
Antes de comenzar con ejemplos más concretos sobre DataView cabría comentar que un objeto DataView es una colección de objetos DataRowView y que cada objeto DataRowView permite el acceso al objeto DataRow que está apuntado a través de su propiedad Row.
Por otro lado la propiedad DataView.Table devuelve el objeto DataTable al que está asociado el objeto DataView.
Finalmente en el objeto DataView ya sí disponemos de más y mejores métodos para la búsqueda, ordenación y filtrado de filas:
- RowFilter
- Sort
- ApplyDefaultSort
- Find
- FindRows
Con la propiedad RowFilter podemos filtrar a una expresión dada.
' Vista por defecto
customersDataTable.DefaultView.RowFilter = "LockOrder = 1"
' Nueva vista
Dim miDataView As New DataView(customersDataTable)
miDataView.RowFilter = "LockOrder = 1"
' Eliminar filtro
customersDataTable.DefaultView.RowFilter = String.Empty
miDataView.RowFilter = String.Empty
Con la propiedad RowStateFilter podemos filtrar filas por un estado concreto según DataViewRowState.
Con la propiedad Sort podemos ordenar el conjunto de resultados de nuestro DataView.
Dim miDataView As New DataView(customersDataTable)
miDataView.Sort = "CompanyID DESC, CustomerID DESC"
El método Find busca según el criterio de ordenación actual y devuelve el primer índice del objeto DataRowView encontrado o -1.
Find necesita que previamente se haya especificado un criterio de ordenación que será sobre el que realice la búsqueda.
Dim miDataView As New DataView(customersDataTable)
miDataView.Sort = "CustomerID"
Dim index As Integer = miDataView.Find("4309463")
Si la ordenación se ha realizado por múltiples campos, será necesario suministrar valores para cada uno de estos campos:
Dim miDataView As New DataView(customersDataTable)
miDataView.Sort = "CompanyID, CustomerID"
Dim values(1) As String
values(0) = "520" ' CompanyID
values(1) = "4309463" ' CustomerID
Dim index As Integer = miDataView.Find(values)
Con el método Find encontramos el primer índice de una fila, pero si lo que queremos es obtener todas las filas que cumplan el criterio especificado (no sólo la primera), necesitamos utilizar el método FindRows.
Dim miDataView As New DataView(customersDataTable)
miDataView.Sort = "LockOrder"
Dim foundRows() As DataRowView = miDataView.FindRows("False")
If foundRows.Length > 0 Then
' TODO Hacer algo
End If
En este ejemplo quizás te extrañe la condición de búsqueda a “False”, pero es así como la expresión de filtro quiere comparar campos de tipo bit.
Por último, la propiedad ApplyDefaultSort indica si se va a utilizar o no el orden predeterminado. Aquí la pregunta es ¿Cuál es el orden predeterminado? Pues es el orden que tuviera el objeto DataTable asociado,y la única manera que hay de establecer este orden en el objeto DataTable es con la propiedad DataTable.PrimaryKey. De este modo, si nuestro objeto DataTable tiene establecida la propiedad PrimaryKey podremos buscar con Find sobre ella, si aplicamos la ordenación por defecto.
Dim primaryKey() As DataColumn = { _
customersDataTable.Columns.Item("CompanyID"), _
customersDataTable.Columns.Item("CustomerID")}
customersDataTable.PrimaryKey = primaryKey
Dim miDataView As New DataView(customersDataTable)
miDataView.ApplyDefaultSort = True
Dim values() As String = {"520", "4309397"}
Dim index As Integer = miDataView.Find(values)
Habiendo ya explicado las propiedades y métodos principales para la búsqueda, ordenación y filtrado de filas en un objeto DataTable, ahora es momento de ver cómo realizar estas mismas tareas con Linq To DataSet.
Lo primero a mencionar es que con Linq To DataSet trabajaremos directamente sobre el objeto DataTable y no utilizaremos nunca el objeto DataView. Además, los conceptos de filtrado y ordenación conviven en la misma expresión de consulta como un sola operación que devuelve un conjunto de resultados.
Dim q As Data.EnumerableRowCollection(Of DataRow)
q =
From r In customersDataTable.AsEnumerable
Where
r.Field(Of String)("CustomerID").StartsWith("4309")
AndAlso r.Field(Of Boolean)("LockOrder") = True
Order By
r.Field(Of String)("CompanyID"),
r.Field(Of String)("CustomerID") Descending
Select r
En este ejemplo, hemos filtrado por una condición compleja y además hemos ordenado los resultados, todo ellos sin importarnos la ordenación predeterminada, si los campos de filtro son parte de la ordenación, etc.
Como podrás comprobar, con Linq To DataSet todo es más sencillo. Si aún no lo has leído, insisto en que visites el post Linq To DataSet para familiarizarte con la sintaxis.
Incluso el anterior código podría ser más compacto si utilizamos la inferencia de tipos (como es habitual) para la variable q y además omitimos la cláusula Select.
Dim q =
From r In customersDataTable.AsEnumerable
Where
r.Field(Of String)("CustomerID").StartsWith("4309")
AndAlso r.Field(Of Boolean)("LockOrder") = True
Order By
r.Field(Of String)("CompanyID"),
r.Field(Of String)("CustomerID") Descending
A partir de aquí podrías iterar sobre el conjunto de resultados para realizar las operaciones que creas oportunas.
For Each r In q
Response.Write(String.Format("CustomerID {0}", r.Item("CustomerID")))
Next
En cualquier caso, la mitad de las veces que utilizo un DataTable es para enlazarlo a un control de datos. Con independencia de que se pueda enlazar la consulta Linq a un control, si queremos seguir enlazando objetos DataView (recuerda que cuando enlazas un DataTable a un control de datos, en realidad estás enlazando DataTable.DefaultView), lo que tenemos que hacer es transformar nuestra consulta a un objeto DataView con el método DataTable.AsDataView.
Este método (que está disponible si agregamos la referencia al ensamblado System.Data.DataSetExtensions), convierte nuestra consulta de Linq To DataSet en un objeto DataView.
Dim q = From r In customersDataTable.AsEnumerable
Return q.AsDataView
El método tiene algunas limitaciones, como que no permite consultas que devuelvan tipos anónimos ni operaciones de combinación.
Por otro lado, también tenemos disponible el método CopyToDataTable que copia las filas del resultado de la consulta en un nuevo objeto DataTable. Es importante reseñar que CopyToDataTable “copia”, mientras que AsDataView no copia sino que “apunta” a filas. Es decir, cambios realizados en las filas del objeto DataView (con AsDataView) se verán reflejados en el objeto DataTable asociado, mientras que con CopyToDataTable estamos hablando de un nueva nuevo DataTable que nada tiene que ver con el original.
Al igual que AsDataView, CopyToDataTable tampoco puede copiar objetos anónimos ni operaciones de combinación, pero para este método podemos encontrar una implementación personalizado en MSDN para salvar estas limitaciones (esto es posible porque “creamos” un nuevo objeto DataTable, mientras que con AsDataView no es posible porque cada objeto DataRowView tiene que apuntar forzosamente a un objeto DataRow).
Aunque me he enrollado mucho con las limitaciones de AsDataView y CopyToDataTable, lo cierto es que si quieres enlazar tu consulta Linq a un control de datos en raras ocasiones querrás utilizar tipos anónimos, así que no hay excusa para no utilizar Linq To DataSet.
Un saludo!
No hay comentarios:
Publicar un comentario