none
problema con EF4 y SELF TRACKING RRS feed

  • Pregunta

  • Buenas tardes a todos los foristas

    despues de tanto pelear con mis vistas y probar muchas cosas donde me encontre con muchos problemas que me erriquecieron, a pesar de las penas que me causaron, ya estoy listo para grabar mis entidades PADRE - HIJO, en este momento tengo problemas porque cuando mis aplicacion solo me esta actualizando los datos de la entidad padre, pero nunca me actualiza los datos de la entidad hija.

    NOTAS IMPORTANTES:

      la clave primaria tanto de PERSONAS como de PROFESORES es el campo ConsecutivoPersona que esta marcado en la base de datos como AUTOINCREMENT

    este es mi codigo actual

    recupero mi entidad padre y sus hijos

     public IEnumerable<Personas> GetPersonasByIdentificacion(int identificacionPersona)
            {
                using (context = new misEntities())
                {
                    var consulta = from persona in context.Personas.Include("Profesores")
                                   where persona.NumeroDocumento == identificacionPersona
                                   select persona;
                    IList<Personas> lista = consulta.ToList();
                    return lista;
                }
            } 

     

    la entidad recuperada la devuelvo a la vista, y se la presento al usuario en 2 formview 

    una vez el usuario me modifica la informacion, da click en un boton "Grabar", en el evento de este boton, yo paso por los formview recuperando la informacion que tienen los objetos de cada formview y creo dos objectentity uno persona y otro profesor donde relaciono la informacion actual de los formview

            protected void BtGrabar_Click(object sender, EventArgs e)
            {
                _persona = obtenerDatosPersona(); //aqui recorro los objetos del formview persona y devuelvo una entidad tipo persona (funciona bien)
                _profesor = obtenerDatosProfesor(); //aqui recorro los objetos del formview profesor y devuelvo una entidad tipo profesor

                //ahora adicionamos la información del profesor a la persona para que se grabe en una sola transaccion
                _persona.Profesores = _profesor;

                if (FormView1.CurrentMode == FormViewMode.Edit)
                {
                    (new ProcesosFacade()).UpdatePersona(_persona); //llamo mi clase que actualiza la entidad persona
                }
     }

     

    esta es mi clase para actualizar

    public void UpdatePersona(Personas currentPersona)
            {
                try
                {
                    using (context = new misEntities())
                    {
                        context.Personas.Attach(currentPersona);
                        context.ObjectStateManager.ChangeObjectState(currentPersona, EntityState.Modified);
                        context.SaveChanges();

                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

     

    ahora bien, como comentaba antes tengo problemas porque solo me esta actualizando la clase PERSONA, pero la información de PROFESOR ni la toca, es decir no la actualiza.

    leyendo en muchos foros, dicen que el problema pasa porque no activo el changetracking

    http://social.msdn.microsoft.com/Forums/es-AR/adodotnetentityframeworkes/thread/e0149b8a-d663-4d57-b602-e19745b84bff

     

    y en otro foro (http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/5789eadd-6322-421a-be16-d6ee7fb198a6/)

    leo que la las intrucciones para grabar con el tracking activado son

     public void UpdatePersona(Personas currentPersona)
            {
                try
                {
                    using (context = new misEntities())
                    {
                        context.Personas.ApplyChanges(currentPersona);
                        context.SaveChanges();                 
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            } 

    pero cuando cambio mi UpdatePersona para que quede como el ejemplo de arriba, tengo problemas porque a pesar que es una persona que ya esta grabada en la base de datos, siempre me genera un nuevo consecutivoPersona (es decir me cambia la clave primaria que ya tiene la entidadPersona) y me saca error diciendo que "ConsecutivoPersona es clave primaria y su valor no puede ser modificado"

    DEDUZCO  que este es comportamiento normal del STE, ya que, sino no lo han notado, manualmente no he activado el CHANGETRACKING y el STE al ver que no hay referencia o informacion anterior de la entidad que estoy pasando para modificar, pues entiende que es una entidad nueva, y trata de adicionarla...

     

    AHORA LAS PREGUNTAS 

    PRIMERA: como y en que momento debo activar el CHANGETRACKING ?

    SEGUNDA: no sé como formular esta pregunata, la idea es que imagino que el changetracking lo debo activar al momento de recuperar los datos, estos datos van a la vista, y luego deben volver al modelo para ser guardados, estoy en lo correcto?

    TERCERA: como paso mis objetos (entidades) del modelo a la vista para mostrarlas y que me regresen con el CHANGETRACKING activado??

    CUARTA Y ULTIMA POR EL MOMENTO: cuales son las intrucciones adecuadas que debo usar para actualizar / adicionar, mis datos y que mejoras debo hacer al codigo que mostre anteriormente ???

    muchas gracias amigos y disculpen lo extenso del post, pero quiero ser lo mas preciso posible, para que la pregunta se entienda y para que otros foristas con el mismo problema se identifiquen y ojala con las respuestas que me den todos podamos seguir avanzando en este apasionante mundo de EF4 (aunque hay momento en lo que he querido tirar la toalla... jajaja)

    un abrazo a todos desde Colombia

    miércoles, 1 de junio de 2011 21:25

Respuestas

  • FINALMENTEEEEEEEEEEEEEEEEEEEEE por fin puedo cerrar el tema de grabar padres e hijos en una sola transaccion.

    el codigo quedó de la siguiente manera y funciona muy bien para mi.

    RECUPERAMOS el registro que queremos modificar, notese que todos los MARKASxxx automaticamente habilitan, encienden, activan el self traking (ver http://msdn.microsoft.com/es-es/library/ff407090.aspx

    public IEnumerable<Personas> GetPersonasByIdentificacion(int identificacionPersona)
            {
                using (context = new misEntities())
                {
                    List<Personas> _list = context.Personas.Include("Estudiantes").Include("Profesores").Where(d => d.NumeroDocumento == identificacionPersona).ToList();
                    foreach (Personas _persona in _list)
                    {
                        _persona.MarkAsModified();
                        if (_persona.Profesores != null)
                        {
                            _persona.Profesores.MarkAsModified();
                        }
                    }

                    return _list;
                }
            }

     

    GRABAR Este es el metodo para grabar, solo se necesita un solo metodo para grabar, independientemente que las operaciones sean UPDATE, INSERT, DELETE o una combinacion entre ellas.  lo importante al trabajar con SELF TRAKING ENTITIES es mantener siempre la pista de las entidades iniciales, es decir si se quiere modificar registros recuperados de la db, inmediatamente se recuperan de la DB, se les marca para modificar, y una vez en la vista (ASP en mi caso) se debe persistir esta misma entidad hasta que regrese al modelo para ser grabada... de lo contrario se le pierde todo el historial de cambios registrados por el STE y la bandera de SelfTracking se desactiva

           public void GrabarPersona(Personas persona)
            {
                using (context = new misEntities())
                {
                    try
                    {
                        context.Personas.ApplyChanges(persona);
                        context.SaveChanges();
                        persona.StopTracking();
                        persona.Profesores.StopTracking();
                    }
                    catch (OptimisticConcurrencyException e)
                    {
                        context.Refresh(RefreshMode.ClientWins, persona);
                        context.SaveChanges();
                    }
                    catch (Exception ex)
                    {
                        throw (ex);
                    }
                }
            }

     

    IMPORTANTE. si lo que se quiere es insertar un registro nuevo, pues simplemente en el boton grabar (o cualquier otro objeto que usen para indicar que se va a guardar) se debe crear la entidad y marcarla para ser adicionada, asi el STE de una vez le activa el selfTracking y se da cuenta que debe ejecutar una operacion de insert en la DB.

    _persona = new Personas().MarkAsAdded(); 

    de igualmanera esta entidad _persona debe ser persisitida hasta que se vaya al modelo para ser guardada, cuando digo persistir, quiero decir guardar ese objeto en cualquier artefacto que no se resetee cuando se hagan los POSTBACK, en mi caso guardo la referencia de mis entidades en variables de session (no uso el ViewState, porque STE no esta diseñado para permitir serializacion binaria, aunque se podría lograr haciendo algunos cambios en la plantilla context.tt, pero ese es otro tema que tiene tanto de largo como de ancho)

     

    algo importante que se me olvidaba, es que a esa entidad padre _persona, se le deben relacionar, adicionar, incorporar, sus hijos, en cualquier parte de la transaccion, en mi caso lo hago en el boton Grabar, y despues de que obtengo todos los datos de mis formview.

    algo como esto.

            obtenerDatosFormviewPersona();
            obtenerDatosFormviewProfesor();
            //ahora adicionamos la información del profesor a la persona para que se grabe en una sola transaccion
            _persona.Profesores = _profesor;
            (new miProcesosFacade()).GrabarPersona(_persona); 

     

    NOTAS FINALES. lo mas importante cuando se trabaja con STE y se quiere aprovechar toda su potencia, es mantener un control riguroso del selftracking que siempre este ON desde que se inicia la transaccion hasta que llegue al modelo para ser guardada, y no menos importante un control riguroso del estado de sus entidades segun lo que se quiera hacer, insert, delete, update, tanto de los padres como de los hijos contenidos en él

     

    FINALMENTE solo agradecer a los foristas que con sus aportes me ayudaron a resolver el tema, y espero que este pequeño instructivo sea de ayuda a alguien mas, y que los que se beneficien de esta información, dejen comentarios de su implementacion y los problemas que se encontraron, y/o mejoras, para que dejen un legado a los que vienen atras y y se les facilite su tarea de aprendizaje  (porque de verdad que es poca la información que hay en español sobre el tema)

    un saludo

    • Marcado como respuesta milson cardona jueves, 2 de junio de 2011 20:40
    jueves, 2 de junio de 2011 20:40

Todas las respuestas

  •  


    Buenos dias foristas.

    les comento que he avanzado en este tema, ya logre que me grabara el padre y el hijo en una sola transaccion, y tambien hace los update de padre e hijos en una sola transaccion. EUREKA ya estoy viendo la luz al final del tunel...

    pero tengo un problema, cuando tengo el padre almacenado y lo recupero de la db, pero este padre no tiene hijos aun, entonces cuando quiero adicionarle el primer hijo a este padre, y grabo me saca el mismo problema del primer post de este hijo, me trata ambas entidades (padre e hijo) como si fueran nuevos registros, y obviamente me saca error porque le cambia la clave primaria al padre, que ya esta almacenado en la db.

    pongo mi código de la clase de negocios

    recupero mis datos...

    como ven activo el STARTTRACTING tanto para el padre como para su hijo si existe.

    public IEnumerable<Personas> GetPersonasByIdentificacion(int identificacionPersona)
     {
     using (context = new misEntities())
     {
     List<Personas> _list = context.Personas.Include("Estudiantes").Include("Profesores").Where(d => d.NumeroDocumento == identificacionPersona).ToList();
     foreach (Personas _persona in _list)
     {
     _persona.StartTracking();
     if (_persona.Profesores != null)
     {
     _persona.Profesores.StartTracking();
     }
     }

     return _list;
     }
     }

     

    NOTA IMPORTANTE: cuando esta entidad llega a mi ASP, tengo que persistirla durante toda la session (al menos durante el tiempo que demore el usuario entre la recuperacion, modificacion y SAVECHANGES), de lo contrario se perderia la informacion del SelfTracking y por ende no grabaria los datos de la manera deseada. (OJO al menos por lo que he podido notar en estas tareas de prueba y error)

    esta es mi clase que almacena los cambios

     public void guardarPersona(Personas persona)
     {
     using (context = new misEntities())
     {
     try
     {
     persona.StopTracking();
     persona.Profesores.StopTracking();
     context.Personas.ApplyChanges(persona);
     context.SaveChanges();
     }
     catch (OptimisticConcurrencyException e)
     {
     context.Refresh(RefreshMode.ClientWins, persona);
     context.SaveChanges();
     }
     catch (Exception ex)
     {
     throw (ex);
     }
     }
     }

     todavia no tengo claro como funciona el OptimisticConcurrencyException, sé que es para tratar de controlar los problemas de concurrencia que se puedan presentar al momento de grabar los datos, pero aun me falta investigar un poco mas a fondo, si alguien me puede dar una explicadita, sería genial

     

    PREGUNTAS:

    PRIMERA que tengo mal en el código que no me graba bien cuando intento adicionarle un hijo nuevo a un padre ya almacenado en la db??

    SEGUNDA esta si es la manera correcta de hacerlo??

    TERCERA alguna recomendación que me puedan hacer??

     

    gracias a todos

    jueves, 2 de junio de 2011 13:24
  • FINALMENTEEEEEEEEEEEEEEEEEEEEE por fin puedo cerrar el tema de grabar padres e hijos en una sola transaccion.

    el codigo quedó de la siguiente manera y funciona muy bien para mi.

    RECUPERAMOS el registro que queremos modificar, notese que todos los MARKASxxx automaticamente habilitan, encienden, activan el self traking (ver http://msdn.microsoft.com/es-es/library/ff407090.aspx

    public IEnumerable<Personas> GetPersonasByIdentificacion(int identificacionPersona)
            {
                using (context = new misEntities())
                {
                    List<Personas> _list = context.Personas.Include("Estudiantes").Include("Profesores").Where(d => d.NumeroDocumento == identificacionPersona).ToList();
                    foreach (Personas _persona in _list)
                    {
                        _persona.MarkAsModified();
                        if (_persona.Profesores != null)
                        {
                            _persona.Profesores.MarkAsModified();
                        }
                    }

                    return _list;
                }
            }

     

    GRABAR Este es el metodo para grabar, solo se necesita un solo metodo para grabar, independientemente que las operaciones sean UPDATE, INSERT, DELETE o una combinacion entre ellas.  lo importante al trabajar con SELF TRAKING ENTITIES es mantener siempre la pista de las entidades iniciales, es decir si se quiere modificar registros recuperados de la db, inmediatamente se recuperan de la DB, se les marca para modificar, y una vez en la vista (ASP en mi caso) se debe persistir esta misma entidad hasta que regrese al modelo para ser grabada... de lo contrario se le pierde todo el historial de cambios registrados por el STE y la bandera de SelfTracking se desactiva

           public void GrabarPersona(Personas persona)
            {
                using (context = new misEntities())
                {
                    try
                    {
                        context.Personas.ApplyChanges(persona);
                        context.SaveChanges();
                        persona.StopTracking();
                        persona.Profesores.StopTracking();
                    }
                    catch (OptimisticConcurrencyException e)
                    {
                        context.Refresh(RefreshMode.ClientWins, persona);
                        context.SaveChanges();
                    }
                    catch (Exception ex)
                    {
                        throw (ex);
                    }
                }
            }

     

    IMPORTANTE. si lo que se quiere es insertar un registro nuevo, pues simplemente en el boton grabar (o cualquier otro objeto que usen para indicar que se va a guardar) se debe crear la entidad y marcarla para ser adicionada, asi el STE de una vez le activa el selfTracking y se da cuenta que debe ejecutar una operacion de insert en la DB.

    _persona = new Personas().MarkAsAdded(); 

    de igualmanera esta entidad _persona debe ser persisitida hasta que se vaya al modelo para ser guardada, cuando digo persistir, quiero decir guardar ese objeto en cualquier artefacto que no se resetee cuando se hagan los POSTBACK, en mi caso guardo la referencia de mis entidades en variables de session (no uso el ViewState, porque STE no esta diseñado para permitir serializacion binaria, aunque se podría lograr haciendo algunos cambios en la plantilla context.tt, pero ese es otro tema que tiene tanto de largo como de ancho)

     

    algo importante que se me olvidaba, es que a esa entidad padre _persona, se le deben relacionar, adicionar, incorporar, sus hijos, en cualquier parte de la transaccion, en mi caso lo hago en el boton Grabar, y despues de que obtengo todos los datos de mis formview.

    algo como esto.

            obtenerDatosFormviewPersona();
            obtenerDatosFormviewProfesor();
            //ahora adicionamos la información del profesor a la persona para que se grabe en una sola transaccion
            _persona.Profesores = _profesor;
            (new miProcesosFacade()).GrabarPersona(_persona); 

     

    NOTAS FINALES. lo mas importante cuando se trabaja con STE y se quiere aprovechar toda su potencia, es mantener un control riguroso del selftracking que siempre este ON desde que se inicia la transaccion hasta que llegue al modelo para ser guardada, y no menos importante un control riguroso del estado de sus entidades segun lo que se quiera hacer, insert, delete, update, tanto de los padres como de los hijos contenidos en él

     

    FINALMENTE solo agradecer a los foristas que con sus aportes me ayudaron a resolver el tema, y espero que este pequeño instructivo sea de ayuda a alguien mas, y que los que se beneficien de esta información, dejen comentarios de su implementacion y los problemas que se encontraron, y/o mejoras, para que dejen un legado a los que vienen atras y y se les facilite su tarea de aprendizaje  (porque de verdad que es poca la información que hay en español sobre el tema)

    un saludo

    • Marcado como respuesta milson cardona jueves, 2 de junio de 2011 20:40
    jueves, 2 de junio de 2011 20:40