Clases de Linq to Sql con DataGridView

Problema: Necesito mostrar los datos de unas clases en dos grid tipo maestro - detalle.
Solución: Utilizar clases genéricas y enlazarlos a los grid usando Linq.

Las clases genéricas es un nuevo tipo de elemento que viene en Visual Studio. Se lo puede encontrar al dar click en el menú Proyecto y luego en Agregar Nuevo Elemento. En la ventana de Agregar Nuevo Elemento, en la Categoría Datos, se encuentra la plantilla "Clases de LINQ to SQL", el cual crea un archivo con la extensión .dbml.

Al cargar este archivo aparecerá un apartado en blanco en donde se podrán ir diseñando las clases con sus propiedades, como quien añade tablas y campos. Dos clases se pueden relacionar por medio de una asociación o por herencia, pero realmente se puede controlar el detalle por medio de sentencias Linq.

Para entender un poco mejor, pongo un ejemplo.

Tengo un contenedor de clases llamado DataClasses1DataContext, el cual contiene dentro dos clases "tipadas" llamadas Class1 y Class2. La clase Class1 tiene dos propiedades Property1 (tipo Integer) y Property2 (tipo String), y la clase Class2 tiene tres propiedades Property1 (tipo Integer), Property2 (tipo Integer) y Property3 (tipo String).
Tengo un formulario llamado Form1, el cual contiene dos grid llamados DataGridView1 y DataGridView2.

El código que se utiliza en el formulario principal es el siguiente.

Public Class Form1
    Private Class1 As New List(Of Class1)
    Private Class2 As New List(Of Class2)

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Se llenan los datos
        llenar_clase1()
        llenar_clase2()

        'Se asignan los datos al grid
        DataGridView1.DataSource = Class1
        'Para que se seleccione toda la fila
        DataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect

        'Se obtiene la primera fila del grid principal y se lo setea
        'al grid detalle por medio de linq
        Dim Clase2 = From clase In Class2 Where clase.Property2 = 1
        Dim clases2 = Clase2.ToList
        DataGridView2.DataSource = clases2
    End Sub

    Private Sub llenar_clase1()
        'Aqui se debe utilizar una funcion para llenar los datos
        'Esta funcion se llena con datos de prueba
        Dim dt_table As New DataTable
        dt_table = dt_clase1()

        'Recorremos los datos temporales para llenar la clase
        For Each dt As DataRow In dt_table.Rows
            'Se crea una instancia nueva de la clase 
            'para llenarlo en la lista
            Dim clase1 As Class1 = New Class1

            'Se pasan los datos
            clase1.Property1 = dt.Item("col1")
            clase1.Property2 = dt.Item("col2")

            'Se llena la lista principal
            Class1.Add(clase1)
        Next
    End Sub

    Private Function dt_clase1() As DataTable
        Dim new_DataTable As New DataTable

        'Creando las columnas
        new_DataTable.Columns.Add("col1")
        new_DataTable.Columns.Add("col2")

        'Crando las filas o datos
        new_DataTable.Rows.Add(1, "AAA")
        new_DataTable.Rows.Add(2, "BBB")
        new_DataTable.Rows.Add(3, "CCC")

        dt_clase1 = new_DataTable
    End Function

    Private Sub llenar_clase2()
        'Aqui se debe utilizar una funcion para llenar los datos
        'Esta funcion se llena con datos de prueba
        Dim dt_table As New DataTable
        dt_table = dt_clase2()

        'Recorremos los datos temporales para llenar la clase
        For Each dt As DataRow In dt_table.Rows
            'Se crea una instancia nueva de la clase 
            'para llenarlo en la lista
            Dim clase2 As Class2 = New Class2

            'Se pasan los datos
            clase2.Property1 = dt.Item("col1")
            clase2.Property2 = dt.Item("col2")
            clase2.Property3 = dt.Item("col3")

            'Se llena la lista principal
            Class2.Add(clase2)
        Next
    End Sub

    Private Function dt_clase2() As DataTable
        Dim new_DataTable As New DataTable

        'Creando las columnas
        new_DataTable.Columns.Add("col1")
        new_DataTable.Columns.Add("col2")
        new_DataTable.Columns.Add("col3")

        'Crando las filas o datos
        new_DataTable.Rows.Add(1, 1, "AAA-AAA")
        new_DataTable.Rows.Add(1, 2, "AAA-BBB")
        new_DataTable.Rows.Add(1, 3, "AAA-CCC")
        new_DataTable.Rows.Add(2, 1, "BBB-AAA")
        new_DataTable.Rows.Add(2, 2, "BBB-BBB")
        new_DataTable.Rows.Add(2, 3, "BBB-CCC")
        new_DataTable.Rows.Add(3, 1, "CCC-AAA")
        new_DataTable.Rows.Add(3, 2, "CCC-BBB")
        new_DataTable.Rows.Add(3, 3, "CCC-CCC")

        dt_clase2 = new_DataTable
    End Function

    Private Sub DataGridView1_CellClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _
    Handles DataGridView1.CellClick
        'Cada vez que cambie de fila, se actualiza el grid de detalle
        Dim Clase2 = From clase In Class2 Where clase.Property2 = e.RowIndex + 1
        Dim clases2 = Clase2.ToList
        DataGridView2.DataSource = clases2
    End Sub
End Class

El Linq es parecido a las sentencias de SQL y se basan en la misma lógica, pero no en el mismo orden.

Una sentencia Linq es parecido a lo siguiente.

Dim valor = From variable In contenedor Where variable = objeto Select variable

valor es el resultado y lo que contiene la sentencia devuelta.
variable es una temporal en donde se procesará la setencia dependiendo de la condición.
contenedor es la clase o el objeto que pueda utilizar una sentencia Linq.
objeto es la restricción que se necesita para obtener un conjunto determinado de datos.

Quienes hayan usado sentencias SQL podrán fácilmente adaptarse al Linq.

Usar clases genéricas o tipadas es muy útil y versatil, sobre todo cuando se necesita trabajar con diferentes fuentes de datos y/o no se puede utilizar los orígenes estándares como DataTables o DataSets.

Su poder se ve apreciado cuando se tiene enlazados más de un maestro - detalle, o se cuenta con una cadena de detalles. Por ejemplo, una cotización que tiene varias facturas y cada factura tiene su cabecera y detalle, esta complejidad se ahorra mucho tiempo de código al "dibujar" las clases y relacionarlas entre sí, siendo el Linq el que facilita la tarea del filtrado de los datos.