none
relacion entre dos datatables RRS feed

  • Pregunta

  • Saludos a todos!

    Necesito relacionar dos tablas, Salas y SalasDocentes (Maestro y detalle respectivamente) Tengo en la capa de datos, dos metodos para devolver los datos de cada una:

    Public Function ObtenerSalas() As DataTable
            Const sqlQuery = "SELECT salas.* FROM salas;"

            Using da As New OleDb.OleDbDataAdapter(sqlQuery, _conectarDB.conn)
                Dim dt As New DataTable
                da.Fill(dt)
                Return dt
            End Using
    End Function

    Public Function ObtenerSalasDocentes() As DataTable

            Const sqlQuery = "SELECT SalasDocentes.*, docentes.NombreDocente " _
                           & "FROM Docentes " _
                           & "INNER JOIN SalasDocentes ON Docentes.idDocente = SalasDocentes.idDocente; "

            Using da As New OleDb.OleDbDataAdapter(sqlQuery, _conectarDB.conn)
                Dim dt As New DataTable
                da.Fill(dt)
                Return dt
            End Using
    End Function


    En un Winform:

    Public Class Form1
        Private _datsalas As New datSalas
        Private WithEvents bsMaestro As New BindingSource
        Private WithEvents bsDetalle As New BindingSource


    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim ds As New DataSet
            Dim dtMaestro As DataTable
            Dim dtDetalle As DataTable

            dtMaestro = _datsalas.ObtenerSalas
            dtDetalle = _datsalas.ObtenerSalasDocentes

            ds.Tables.Add(dtMaestro)
            ds.Tables.Add(dtDetalle)

            Dim rel As DataRelation = New DataRelation("Relacion", ds.Tables("dtMaestro").Columns("idSala"), ds.Tables("dtDetalle").Columns("idSala"))

    ds.relations.Add(rel)
            bsMaestro.DataMember = "dtMaestro"
            bsMaestro.DataSource = ds
            bsDetalle.DataSource = bsMaestro
            bsDetalle.DataMember = "rel"

            dgvMaestro.DataSource = bsMaestro
            dgvDetalle.DataSource = bsDetalle

        End Sub

    end class

    Al ejecutar el formulario, me sale el error System.NullReferenceException: 'Referencia a objeto no establecida como instancia de un objeto.' en la linea donde establezco la relacion.

    En que me estoy equivocando? gracias!




    • Editado ClauBC domingo, 4 de junio de 2017 22:07
    domingo, 4 de junio de 2017 21:46

Respuestas

  • - cuando corro el form y me cambio entre registros, no actualiza la relacion como suponia que lo haria.

    No, no funciona porque estás vinculando dos datagrids distintos a las dos tablas del dataset. El dataset no tiene el concepto de "registro actual" para poder actualizar el binding sobre el otro grid. Solo funciona si vinculas el dataset completo (no sus tablas por separado) a un único grid en Winforms, en cuyo caso al hacer click en un registro de la tabla maestra te lo expande y te muestra los datos relacionados de la tabla hija (porque el Grid sabe seguir la relación, no porque el dataset lo sepa).

    Si lo quieres hacer con dos grids separados, necesitas un componente externo que comande el vínculo de datos. En Winforms, se usa para esto el BindingDataSource, que si lo configuras bien lo puedes "bindear" al dataset, y luego te ofrece las dos tablas para bindearlas a los grids. En este caso, al cambiar entre registros sí que se aplica la relación en los grids, porque es el BindingDataSource el que la aplica. Nota: para que esto funcione hay que usar UN ÚNICO BindingSource que se conecte al DataSet, no dos bindingsources vinculados cada uno a una tabla, como parece que hay en tu código si es que el prefijo "bs" que tienen tus dos variables se refiere a que son bindingsources.

    - como llevar el codigo al modelo de capas, que originalmente presente.

    Podrías pasarle el ds como argumento y dejar que los métodos de la capa de datos te añadan un dt a ese ds, en lugar de devolver el dt suelto. O podrías ponerles nombre a los dt cuando les hagas el "Add" al Dataset. O podrías indexarlas por número en lugar de por nombre.

    • Marcado como respuesta ClauBC lunes, 5 de junio de 2017 22:52
    lunes, 5 de junio de 2017 17:26
  • Entiendo lo que me dices, pero pense que parte del codigo que esta arriba antes de establecer la relacion, cargaba las tablas que devuelvo en dtMaestro y dtDetalle, fijate que despues las cargo al dataset. [...]

     ds.Tables.Add(dtMaestro)
     ds.Tables.Add(dtDetalle)

    No, eso no les pone nombre. Eso añade dos datasets en las posiciones 0 y 1 de la colección de tablas del dataset, y dentro del dataset los dos datatables no tienen nombre, solo tienen número (a no ser que previamente les hubieras puesto nombre antes de hacer el Add).
    • Marcado como respuesta ClauBC lunes, 5 de junio de 2017 22:52
    lunes, 5 de junio de 2017 17:31

Todas las respuestas

  • Prueba esto a ver si va
    Dim rel As DataRelation
    rel = New DataRelation("Relacion",ds.Tables("dtMaestro").Columns("idSala"), ds.Rables("dtDetalle").Columns(idSala"))

     Así estás estableciendo la instancia

    • Editado Marcelo PF lunes, 5 de junio de 2017 1:01
    lunes, 5 de junio de 2017 0:56
  • Lo hago en la misma línea, asi y todo probé tu sugerencia, pero sigue igual.
    lunes, 5 de junio de 2017 1:49
  • El problema es que estás referenciando las tablas por nombre, pero no les has puesto nombre.

    Aquí:

    Dim rel As DataRelation = New DataRelation("Relacion", ds.Tables("dtMaestro").Columns("idSala"), ds.Tables("dtDetalle").Columns("idSala"))

    Fíjate que pones  ds.Tables("dtMaestro") y lo mismo con la otra tabla. Pero en ds, la colección Tables no tiene nombre para las tablas, porque las cargas así:

    da.Fill(dt)

    Si la quisieras con nombre tendrías que poner

    da.Fill(dt, "dtMaestro")

    El nombre NO se infiere automáticamente a partir del nombre de la tabla que había en la SELECT. Tienes que indicarlo expresamente. O, si no lo quieres poner, podrías referenciar las tablas por su número de orden: ds.Tables(0) y ds.Tables(1). Pero eso tiene el inconveniente de que depende del orden en que las cargues en el dataset.

    lunes, 5 de junio de 2017 6:09
  • Entiendo lo que me dices, pero pense que parte del codigo que esta arriba antes de establecer la relacion, cargaba las tablas que devuelvo en dtMaestro y dtDetalle, fijate que despues las cargo al dataset. 

     dtMaestro = _datsalas.ObtenerSalas

     dtDetalle = _datsalas.ObtenerSalasDocentes

     ds.Tables.Add(dtMaestro)
     ds.Tables.Add(dtDetalle)

     Dim rel As DataRelation = New DataRelation("Relacion", ds.Tables("dtMaestro").Columns("idSala"), ds.Tables("dtDetalle").Columns("idSala"))

    ds.relations.Add(rel)

    Voy a probar cambiando a lo que tu me dices, te agradezco tu tiempo.

    lunes, 5 de junio de 2017 12:22
  • Cambie el codigo al siguiente, y logre superar los mensajes de error:

    Dim ds As New DataSet
            Dim dtMaestro As New DataTable
            Dim dtDetalle As New DataTable

            Const sqlQuery = "SELECT salas.*" _
                           & "FROM salas;"

            Using da As New OleDb.OleDbDataAdapter(sqlQuery, _conectarDB.conn)
                Dim dtSalas As New DataTable
                da.Fill(ds, "dtSalas")
            End Using

            Const sqlQuery1 = "SELECT SalasDocentes.*, docentes.NombreDocente " _
                           & "FROM Docentes " _
                           & "INNER JOIN SalasDocentes ON Docentes.idDocente = SalasDocentes.idDocente; "


            Using da As New OleDb.OleDbDataAdapter(sqlQuery1, _conectarDB.conn)
                Dim dtSalasDocentes As New DataTable
                da.Fill(ds, "dtSalasDocentes")
            End Using

            Dim rel As DataRelation = New DataRelation("Relacion", ds.Tables("dtSalas").Columns("idSala"), ds.Tables("dtSalasDocentes").Columns("idSala"))

            ds.Relations.Add(rel)

            bsMaestro.DataSource = ds
            bsMaestro.DataMember = "dtSalas"

            bsDetalle.DataSource = bsMaestro
            bsDetalle.DataMember = "relacion"

            dgvMaestro.DataSource = bsMaestro
            dgvDetalle.DataSource = bsDetalle

    pero 2 cosas:

    - cuando corro el form y me cambio entre registros, no actualiza la relacion como suponia que lo haria.

    - como llevar el codigo al modelo de capas, que originalmente presente.

    Bueno, ire por el lado de tirar una consulta a la base de datos cuando cambio de registro y me olvidare de la relacion.

    Si alguien me echa luz a estas 2 cosas que menciono, se los agradeceria. Saludos y gracias!

    lunes, 5 de junio de 2017 12:42
  • - cuando corro el form y me cambio entre registros, no actualiza la relacion como suponia que lo haria.

    No, no funciona porque estás vinculando dos datagrids distintos a las dos tablas del dataset. El dataset no tiene el concepto de "registro actual" para poder actualizar el binding sobre el otro grid. Solo funciona si vinculas el dataset completo (no sus tablas por separado) a un único grid en Winforms, en cuyo caso al hacer click en un registro de la tabla maestra te lo expande y te muestra los datos relacionados de la tabla hija (porque el Grid sabe seguir la relación, no porque el dataset lo sepa).

    Si lo quieres hacer con dos grids separados, necesitas un componente externo que comande el vínculo de datos. En Winforms, se usa para esto el BindingDataSource, que si lo configuras bien lo puedes "bindear" al dataset, y luego te ofrece las dos tablas para bindearlas a los grids. En este caso, al cambiar entre registros sí que se aplica la relación en los grids, porque es el BindingDataSource el que la aplica. Nota: para que esto funcione hay que usar UN ÚNICO BindingSource que se conecte al DataSet, no dos bindingsources vinculados cada uno a una tabla, como parece que hay en tu código si es que el prefijo "bs" que tienen tus dos variables se refiere a que son bindingsources.

    - como llevar el codigo al modelo de capas, que originalmente presente.

    Podrías pasarle el ds como argumento y dejar que los métodos de la capa de datos te añadan un dt a ese ds, en lugar de devolver el dt suelto. O podrías ponerles nombre a los dt cuando les hagas el "Add" al Dataset. O podrías indexarlas por número en lugar de por nombre.

    • Marcado como respuesta ClauBC lunes, 5 de junio de 2017 22:52
    lunes, 5 de junio de 2017 17:26
  • Entiendo lo que me dices, pero pense que parte del codigo que esta arriba antes de establecer la relacion, cargaba las tablas que devuelvo en dtMaestro y dtDetalle, fijate que despues las cargo al dataset. [...]

     ds.Tables.Add(dtMaestro)
     ds.Tables.Add(dtDetalle)

    No, eso no les pone nombre. Eso añade dos datasets en las posiciones 0 y 1 de la colección de tablas del dataset, y dentro del dataset los dos datatables no tienen nombre, solo tienen número (a no ser que previamente les hubieras puesto nombre antes de hacer el Add).
    • Marcado como respuesta ClauBC lunes, 5 de junio de 2017 22:52
    lunes, 5 de junio de 2017 17:31
  • Muchas gracias por tu ayuda. Voy a poner en consideración tus orientaciones. Gracias!
    • Marcado como respuesta ClauBC lunes, 5 de junio de 2017 22:52
    • Desmarcado como respuesta ClauBC lunes, 5 de junio de 2017 22:52
    lunes, 5 de junio de 2017 22:52