none
Modificación de entidades hijas. ¿qué estado tienen? ¿cómo se modifican? RRS feed

  • Pregunta

  • Muy buenas,

    Estoy teniendo una pequeña odisea con mi querido (cada día más, por suerte) Entity Framework. Os expongo el tema:

    Tengo una entidad padre (TableMapping) que tiene una sociación de 1 a * con una entidad hija (SourceSchema). Es decir, una entidad TableMapping puede tener múltiples SourceSchemas.Vamos, un maestro-detalle de toda la vida.

    Bien, para dar de alta esta estructura, no tengo problemas. El problema viene cuando quiero actualizar la estructura, y entran en juego las entidades hijas, ya que puedo modificar las ya existentes, eliminarlas o añadir nuevas.

    En ese caso (la entidad padre no se modifica o como mucho se actualiza, sin problemas) lo que hago es, una vez que en el formulario se han cargado los datos de la entidad padre e hija, y cuando el usuario ha realizado las modificaciones que ha querido, genero una nueva entidad Padre, y le añado entidades hijas nuevas, con los datos que hay en el formulario. Esto tiene como resultado que todas las entidades del grafo están en estado "Detached"

    El problema viene en que no se diferenciar, dentro de las entidades hijas, si estas han sido modificadas, añadidas o eliminadas... y sinceramente, no se muy bien cómo hacerlo.

    No se si la aproximación que estoy realizando es la mejor, sobre todo porque en otros muchos ejemplos que he visto por internet, se suelen utilizar objetos BindingSource... que yo no quiero utilizar.

    Quizá la solución pase por mantener durante toda la vida del formulario un objeto del tipo Padre, y realizar las modificaciones siempre sobre él... Una vez realizados los cambios, lo pasamos al contexto para que actualice o haga lo que necesite y listo... Se me acaba de ocurrir.

    Bueno, si a alguien se le ocurre alguna idea, estaré encantado de probarla.

    Gracias!

     

    miércoles, 29 de diciembre de 2010 15:07

Respuestas

  • Hola Antonio,

    si activas al ChangeTracking por defecto en el contexto (que creo que es así), tan solo tienes que obtener tus entidades de la grid y hacer un SaveChanges sin más, no necesitas marcarlas como modificadas ni nada, si tienes STE esto lo hace la entidad por su cuenta.

     


    Alberto Diaz Martin twitter://@adiazcan | http://geeks.ms/blogs/adiazmartin
    martes, 4 de enero de 2011 9:58
  • Hola a "todos"

    El tema está resuelto. Efectivamente, si activo el changeTracking de la entidad padre, y lo que hago es trabajar con el grafo "activo", no es necesario marcar las entidades como añadidas, modificadas etc. El changeTracking hay que activarlo para cada entidad STE que componga el grafo.

    Con todo lo que hemos sacado de este pequeño prototipo, voy a realizar una serie de posts en nuestro blog, de cómo crear un formulario maestro detalle con STE, teniendo las entidades STE en un ensamblado aparte.

    Además, me va a servir para un proyecto real, que pondremos en producción durante el mes de Febrero.

    Muchas gracias a todos!

    Un saludo,
    Antonio.

    martes, 4 de enero de 2011 11:21

Todas las respuestas

  • Hola Antonio,

    si lo que necesitas saber es si las entidades hijas son nuevas, modificadas, deberías de mantener el context de EF vivo en el formulario o utilizar SelfTracking para que las entidades tengan esa información.

    Otra cosa que se me ocurre serían enlazar los controles y que todo el formulario se mostrara utilizando la entidad padre, haciendo luego mas modificaciones, inserciones y eliminaciones en esta entidad y no en el formulario.


    Alberto Diaz Martin twitter://@adiazcan | http://geeks.ms/blogs/adiazmartin
    jueves, 30 de diciembre de 2010 8:44
  • Hola Alberto,

    Voy a probar la segunda opción, es decir, tener una entidad padre instanciada durante toda la vida del formulario y realizar las modificaciones en ella. Es que además estoy realizando la aplicación en capas, por lo que el contexto a priori creo que no debería ser visible desde el formulario. Eso si, no descarto en absoluto (de hecho lo realizaré más adelante) implementar entidades STE para hacerme la vida un poco más sencilla. La idea principial es trastear en condiciones con EF... y desde luego que lo estoy haciendo!

    Hoy pruebo esa aproximación y contaré por aqui que tal.

    Un saludo y gracias!
    Antonio.

    jueves, 30 de diciembre de 2010 9:38
  • Hola Alberto,

    Me acabo de dar cuenta de que también tengo el mismo problema con la entidad Padre. Lo estoy resolviendo gracias a que en el caso de la padre, tengo un campo Identity como clave, que en vaso de nuevos registros carece de valor. Sin embargo, en el caso de una entidad que no tiene ese tipo de campo, sino que tiene una clave por un campo nvarchar(15), cuando creo la entidad, ya sea mediante un New Entidad o mediante Entidad.Create<nombreEntidad>() las entidades se crean siempre como Detached. ¿existe algún mecanismo o patrón que me ayude a saber si son nuevas en el contexto y por ende en base de datos o si por el contrario existen ya en él? creo que esa respuesta me ayudará también con el tema de las entidades hijas.

    Gracias de nuevo!

    Un saludo,
    Antonio.

    jueves, 30 de diciembre de 2010 12:17
  • Hola Antonio,

    EF es transparente de inserciones o modificaciones. El propio contexto se encarga de comprobar si la entidad es nueva o tiene que ser modificada. ¿por qué necesitas esta propiedad?

    Si trabajas en varias capas, desconectado del contexto, creo que deberías de usar algún patrón, como Self-Tracking, que se encargue de gestionar los cambios en la entidad, sin necesidad de un contexto.

     


    Alberto Diaz Martin twitter://@adiazcan | http://geeks.ms/blogs/adiazmartin
    jueves, 30 de diciembre de 2010 12:59
  • Hola Alberto,

    Efectivamente estoy trabajando en varias capas, con lo que en la capa donde se crean las nuevas entidades no tengo el contexto. Entiendo que eso no es problemático, peero, al no trabajar mediante WCF, tengo que marcar yo el comienzo del tracking de cambios de las entidades (si no estoy equivocado...).

    Si me pasas un ejemplo o me dices dónde puedo encontrar información sobre el patrón Self-Tracking le echo un vistazo. Voy a poner un poco de código en el que recojo los datos del formulario para pasarlos a una entidad STE. esta entidad es Order, y tiene una colección de OrderLines. Es un proyecto de prueba que estoy haciendo y en el que tengo varias capas. En una de ellas están las entidades STE (generadas siguiendo instrucciones de tu blog), para que el resto de capas las tengan accesibles.

    Hay algún método que cambiare a la capa de negocio, así como la consulta que hago para comprobar si la línea ya existe en la colección. Si le he dado muchas patadas a algún patron, siempre son bienvenidas las críticas!!

    la entidad _order está declarada a nivel del formulario, por comodidad.

    Bueno, aqui va:

      Private Sub GetFormContent()
    
        Dim orderService As New Domain.MainModule.OrderService
    
        If String.IsNullOrEmpty(txtOrderID.Text) Then
          ' La entidad es nueva
          _order = New Infraestructure.Data.Entities.Order
        Else
          ' La entidad existe, la recuperamos de base de datos/contexto
          _order = orderService.GetOrder(txtOrderID.Text)
        End If
    
        With _order
    
          .ChangeTracker.ChangeTrackingEnabled = True
    
          If Not String.IsNullOrEmpty(txtOrderID.Text) Then
            .OrderID = txtOrderID.Text
          End If
          .OrderDate = txtOrderDate.Text
          .CustomerID = txtCustomerID.Text
    
          If dgvOrderLines.Rows.Count > 0 Then
    
            For Each row As DataGridViewRow In dgvOrderLines.Rows
              Dim gridOrderID As Integer = row.Cells("OrderID").Value
              Dim gridOrderLineID As Integer = row.Cells("OrderLineID").Value
              Dim gridProductID As String = row.Cells("ProductID").Value
              Dim gridUnits As Integer = row.Cells("Units").Value
              Dim orderLine As Infraestructure.Data.Entities.OrderLine
    
              ' Comprobamos el valor de product, para ver si interesa algo de la grilla
              If Not String.IsNullOrEmpty(gridProductID) Then
    
                ' Si tenemos algo en la colección de orderLines, hay que añadir y/o actualizar
                If _order.OrderLines.Any Then
    
                  ' Si localizamos al elemento por su ID de línea, es que existe en base de datos y hay que actualizarlo
                  If Not _order.OrderLines.FirstOrDefault(Function(c) c.OrderLineID = gridOrderLineID) Is Nothing Then
    
                    'Accedemos al elemento en la colección y actualizamos.
                    orderLine = _order.OrderLines.Single(Function(c) c.OrderLineID = gridOrderLineID)
                    orderLine.ProductID = gridProductID
                    orderLine.Units = gridUnits
                    orderLine.ChangeTracker.AcceptChanges()
    
                  Else
    
                    ' El elemento no está en la colección del objeto, creamos uno nuevo y añadimos.
                    orderLine = New Infraestructure.Data.Entities.OrderLine
                    With orderLine
                      .OrderID = gridOrderID
                      .OrderLineID = gridOrderLineID
                      .ProductID = gridProductID
                      .Units = gridUnits
                    End With
    
                    _order.OrderLines.Add(orderLine)
                    orderLine.ChangeTracker.AcceptChanges()
    
                  End If
    
                End If
    
              End If
    
            Next
    
          End If
    
        End With
    
    Lo que tampoco me queda claro es por qué los metodos extensores de STE no los tengo disponibles desde el código cliente (MarkAs.. StartTracking, etc). En teoría, si no utilizo WCF, tengo yo que marcar de alguna manera (mediante el método AcceptChanges) que la entidad STE ha sido modificada, añadida o eliminada ¿no?

    pongo también el código que utiliza el método Update, para actualizar una entidad STE por si está como el ...

        Using context As New Infraestructure.Data.DataContext
          Dim orderRepository As New Infraestructure.Data.OrderRepository(context)
          order.MarkAsModified()
          orderRepository.Update(order)
          ' Comprobamos líneas
          If order.OrderLines.Any Then
            ' Recorremos colección de líneas
            For Each OrderLine As OrderLine In order.OrderLines
              If OrderLine.OrderLineID = 0 Then
                OrderLine.MarkAsAdded()
                orderRepository.AddOrderLine(OrderLine)
              Else
                OrderLine.MarkAsModified()
                orderRepository.UpdateOrderLine(OrderLine)
                OrderLine.AcceptChanges()
              End If
            Next
          End If
          context.SaveChanges()
    
        End Using
    

    Tambien pongo el método Update del repositorio, para tener una visión más global del proyecto. Ahora mismo está la capa de UI, la capa de negocio y el "DAL", utilizando todas ellas entidades STE.

    Public Class OrderRepository<br/>
    <br/>
      Private _context AS Infraestructure.Data.DataContext<br/>
    <br/>
      Public sub New(context as Infraestructure.Data.DataContext)<br/>
        _context = context<br/>
      End Sub<br/>
    <br/>
      Public Sub Update(ByVal order As Order)
    
        _context.Orders.ApplyChanges(order)
    
      End Sub
    
      Public Sub UpdateOrderLine(ByVal orderLine As OrderLine)
    
        _context.OrderLines.ApplyChanges(orderLine)
    
      End Sub<br/>
    <br/>
    End Class
    
    En fin, no se si voy muy mal encaminado... he logrado dar de alta entidades, y sin problema... pero la modificación se me ha atragantado.. entiendo que porque me falta entender bien el mecanismo de gestión de cambios de las STE y su objeto

    Bueno, gracias de nuevo!!

    Un saludo, Antonio.

    sábado, 1 de enero de 2011 19:14
  • Hola Antonio,

    te dejo un paso a paso que tengo en mi blog sobre STE. http://geeks.ms/blogs/adiazmartin/archive/2010/05/11/entity-framework-self-tracking-paso-a-paso.aspx

    El patrón STE es independiente de WCF, no deberías de tener ningún incoveniente si usas WCF o no.


    Alberto Diaz Martin twitter://@adiazcan | http://geeks.ms/blogs/adiazmartin
    domingo, 2 de enero de 2011 10:41
  • Hola Alberto,

    Este paso a paso fue el que seguí para obtener las entidades STE, está muy bien explicado y es sencillo. Como te comento, tengo el problema en actualizar las entidades sobre base de datos... no se si estoy haciendo algo mal.

    Gracias!

    Un saludo, Antonio.

    domingo, 2 de enero de 2011 13:10
  • Si te parece, me envías el proyecto (lo subes a Skydrive o algo así) y le hecho un vistazo. No deberías de tener grandes problemas con esto.
    Alberto Diaz Martin twitter://@adiazcan | http://geeks.ms/blogs/adiazmartin
    domingo, 2 de enero de 2011 19:23
  • Hola Alberto,

    MIL gracias!!! yo utilizo DropBox. Te paso un enlace público. está el proyecto y un backup de la base de datos.

    http://dl.dropbox.com/u/6003148/PruebasMaestroDetalle.rar

    De verdad, mil gracias. Tengo el libro de EF de Krasis, y voy a comprar hoy el nuevo que acaban de sacar de EF 4.0. He visto que en ese último viene STE. Tengo muchas ganas de aprender a utilizarlo bien!

    Un saludo, Antonio.

    lunes, 3 de enero de 2011 14:28
  • Hola Antonio,

    acabo de hacer una prueba con tu código y si realizar modificaciones. ¿Cuál es el caso que falla?

    Una cosa que he visto en el código es que para actualizar haces una consulta a la bd para obtener la entidad que quieres actualizar, si no me equivoco, no te hace falta hacer el query, la entidad la puedes obtener del objeto almacenado en la fila seleccionada.

     


    Alberto Diaz Martin twitter://@adiazcan | http://geeks.ms/blogs/adiazmartin
    lunes, 3 de enero de 2011 18:32
  • Hola Alberto,

    Las modificaciones de las líneas de pedido. Cuando doy de alta o modifico la cabecera del pedido no hay problema, pero si modifico por ejemplo el productid o units de alguna de las líneas, no actualiza en bd. Por otro lado, ¿de que manera puedo obtener el objeto almacenado en la fila? Lo intenté pero no supe hacerlo.

    Mil gracias de nuevo, Alberto.

    Un saludo, Antonio.

    lunes, 3 de enero de 2011 18:39
  • Mira, para obtener la entidad de la grid utiliza lo siguiente:

    Dim

     

     

    _order As Infraestructure.Data.Entities.Order = dgvOrders.SelectedRows(0).DataBoundItem

    Una cosa que he visto en el código, ¿no sería más fácil hacer un update de la Order y de cada una de las OrderLine por separado?

    Puedes detectar si han habido cambios y ejecutar el update cuando haga falta.


    Alberto Diaz Martin twitter://@adiazcan | http://geeks.ms/blogs/adiazmartin
    lunes, 3 de enero de 2011 19:08
  • Hola Alberto,

    Gracias por tu ayuda! Cambiaré el código para obtener las entidades de esa manera. Me servirá para obtener las entidades cuando el grid está enlazado a datos.

    En cuanto a los updates, pensé que como STE y EF permiten guardar la entidad "padre" de una sola vez, sería más sencillo realizar los updates de esa manera... aunque parece que no es así. ¿podrías ponerme un ejemplo de cómo haces los updates en esos casos de parent-childs? voy a seguir cambiando el código.

    Y lo dicho, mil gracias!

    Un saludo, Antonio.

    martes, 4 de enero de 2011 9:29
  • Hola Antonio,

    si activas al ChangeTracking por defecto en el contexto (que creo que es así), tan solo tienes que obtener tus entidades de la grid y hacer un SaveChanges sin más, no necesitas marcarlas como modificadas ni nada, si tienes STE esto lo hace la entidad por su cuenta.

     


    Alberto Diaz Martin twitter://@adiazcan | http://geeks.ms/blogs/adiazmartin
    martes, 4 de enero de 2011 9:58
  • Hola de nuevo,

    Parece que he conseguido varios avances, sobre todo después de ver los métodos extensores (MarkAs<>, StartTracking, StopTracking, etc...). Teniendo en cuenta eso, y los métodos ApplyChanges (del contexto, de los objectSets..), parece que el tema está funcionando!!

    Voy a realizar unas cuantas pruebas más y cuento por aqui.

    Muchas gracias por tu ayuda!

    martes, 4 de enero de 2011 11:08
  • Hola a "todos"

    El tema está resuelto. Efectivamente, si activo el changeTracking de la entidad padre, y lo que hago es trabajar con el grafo "activo", no es necesario marcar las entidades como añadidas, modificadas etc. El changeTracking hay que activarlo para cada entidad STE que componga el grafo.

    Con todo lo que hemos sacado de este pequeño prototipo, voy a realizar una serie de posts en nuestro blog, de cómo crear un formulario maestro detalle con STE, teniendo las entidades STE en un ensamblado aparte.

    Además, me va a servir para un proyecto real, que pondremos en producción durante el mes de Febrero.

    Muchas gracias a todos!

    Un saludo,
    Antonio.

    martes, 4 de enero de 2011 11:21
  • Genial!! recuerda dejarnos el link del blog para echarle un vistazo al artículo ;)
    Alberto Diaz Martin twitter://@adiazcan | http://geeks.ms/blogs/adiazmartin
    martes, 4 de enero de 2011 11:23
  • hola antonio si pudieras darme el link dode publicaste la solución de este problema porque yo estoy en la misma tengo dias por solucionarlo y nada.

     

    tengo un proyecto en windows form con una  arquitectura N-Capas con entity framework 4.0. para el cual es generado clases POCO.

    mi problema esta en que no tengo ni idea de como saber cuando se a elimino un items del detalle de un pedido para hacerlo persistir a la base de datos.

    gracias..

    lunes, 26 de septiembre de 2011 3:22
  • Hola Misael,

    Al final no publique nada (falta de tiempo, como de costumbre), pero si que tengo el código de la mini apliación que hice. En vez de entidades POCO, son STE, pero imagino que funcionarán igual. Voy a ver si puedo subirlo a mi Skydrive y pego el link por aqui. Espero que lo tengas mañana!

    Espero que te sirva.

    Un saludo!

    Antonio.

    lunes, 26 de septiembre de 2011 13:46
  • gracias Antonio por contestar. Esperare el link.
    lunes, 26 de septiembre de 2011 14:18
  • Hola Misael,

    Este es el enlace. Espero que te sirva! Si no, postea y veremos que podemos hacer.

    https://skydrive.live.com/redir.aspx?cid=31ff6d110838625b&resid=31FF6D110838625B!683&authkey=sGg!8ivcn74%24

    Un saludo, Antonio.

    lunes, 26 de septiembre de 2011 16:15