none
Orden de ejecucion de las instrucciones SQL en SaveChanges RRS feed

  • Pregunta

  • Hola,

    Tengo un proyecto que usa Entity Framework 4.0 usando STE's con plantillas T4, basandose en el proyecto nlayerapp.

    Mi situación es que tengo una tabla con una clave unica (pongamos que se rige por una fecha), cosa que no es nada raro. Entonces la secuencia de pasos que sigo es la siguiente:

    • Leo la tabla (que es un agregado de una entidad principal)
    • Elimino una fila cuya fecha es 01/01/2012 por ejemplo
    • Modifico otra de las filas para que la fecha sea 01/01/2012
    • Luego aplico el SaveChanges, y en el ObjectStateManager esta la entidad principal como Modified, la fila modificada como Modified y la fila eliminada como Deleted. Esto es correcto a mi modo de ver.

    El problema es que el SaveChanges me da una excepcion de clave unica, con lo que entiendo que primero ejecuta los update's y luego el delete.

    Hay algun modo de controlar el orden de ejecucion de las instrucciones SQL o algun modo de evitar este tipo de errores?


    Vota mi respuesta si te ha sido útil. Gracias.


    martes, 3 de abril de 2012 11:12

Respuestas

  • Hola David,

    La verdad que mil perdones por entender que estabas modificando la PK, y solo por eso y en respuesta a tus necesidades acabo de escribir un post en tu honor, así que efectivamente como dices al primcipio ejecuta "Update,Delete,Insert" cuando debería de ser al reves "Delete,Update,Insert". Aunque si lo hiciese de esa forma en algún escenario se puede producir lo mismo.

    Te paso el link para que por lo menos lo leas:) 

    Entity Framework y los indices únicos 

    Saludos,


    phurtado
    Mi Blog Blog
    Sigueme en Twitter

    • Marcado como respuesta David Peláez jueves, 5 de abril de 2012 17:43
    martes, 3 de abril de 2012 22:38

Todas las respuestas

  • La verdad es que no te puedo decir si puedes o no alterar el orden de las sentencias que ejecuta internamente el EF para actualizar los datos. Por lo que he leido parece que no puedes pero tampoco lo puedo asegurar.

    Aquí hablan de esto 

    http://stackoverflow.com/questions/7335582/dbcontext-savechanges-order-of-statement-execution 

    Mi pregunta es, ¿no puede hacer un SaveChanges después de borrar el registro? Algo tal que así

    • Leo la tabla (que es un agregado de una entidad principal)
    • Elimino una fila cuya fecha es 01/01/2012 por ejemplo
    • Aplico SaveChanges
    • Modifico otra de las filas para que la fecha sea 01/01/2012
    • Luego aplico el SaveChanges, y en el ObjectStateManager esta la entidad principal como Modified, la fila modificada como Modified y la fila eliminada como Deleted. Esto es correcto a mi modo de ver.


    Atentamente, Sergio.

    Blog
    Twitter

    martes, 3 de abril de 2012 11:25
  • Por lógica como comentas la solución es hacer un SaveChanges tras cada cambio pero no puedo hacer eso ya que es una aplicación donde el usuario aplica todos los cambios que quiere y luego los acepta. Es más hasta ahora no se habia dado este caso que sinceramente tampoco es lo más habitual.

    Gracias por el enlace, lo habia visto pero me supondria replantear la aplicación entera como tu solución. Pero se agradecen las ideas.


    Vota mi respuesta si te ha sido útil. Gracias.

    martes, 3 de abril de 2012 11:35
  • Hola,

    Tres puntos:

    1. Cuando dices que tienes una clave unica y la intentas modificar, no obtienes la excepcion The property 'NombreColumna' is part of the object's key information and cannot be modified. ??
    2. El orden de la ejecucion de estas instruicciones es: Primero el update y luego si el Delete. Cuando tengas este tipo de dudas es bueno levantar un profiler y mirar que pasa bajo la Base de datos: http://nicolocodev.wordpress.com/2012/01/23/entity-framework-lazy-load/
    3. No, hasta donde se no se puede modificar el orden de ejecucion de estas, solo puedes si ejecutas un savechanges por cada instruccion.

    Saludos.


    Nicolás Herrera
    Bogotá - Colombia
    BLOG - Leader Group BogotaDotNet
    "Daría todo lo que sé, por la mitad de lo que ignoro." Rene Descartes

    martes, 3 de abril de 2012 15:58
  • Hola David,

    Mi situación es que tengo una tabla con una clave unica (pongamos que se rige por una fecha), cosa que no es nada raro.

    Te voy a contestar rápido, si es raro y no se debería de hacer nunca, piensa que aunque estés trabajando con EF, al final como bien sabes eso no involucra otra cosa que una "update" y un "delete" en tu bb.dd y yo hasta el momento siempre he evitado modificar una PK. Lo que he hecho es crear un indice unico en la tabla y entonces es este el que modifico, pero no la PK. 

    Piensa en dos escenarios.

    1. Esa tabla la tienes relacionada con una segunda tabla cuando modificas la tabla tendrías que hacer un update en cascada feo:).

    2. Imagina dos servidores y una replicación entre ambos se van a producir dos viajes uno con los valores antiguos y otro con los nuevos.

    En definitiva nunca se debería de modificar una PK.

    Saludos,


    phurtado
    Mi Blog Blog
    Sigueme en Twitter

    martes, 3 de abril de 2012 16:20
  • En ningun momento dije que modificara una PK, como ya dije es una clave unica o indice unico como tu dices, donde especifico que solamente puede existir esa fecha una vez.

    Entonces si elimino la fila con fecha A, y la fila con fecha B la modifico a A es cuando tengo el error debido al orden de ejecucion (Update-Delete)


    Vota mi respuesta si te ha sido útil. Gracias.

    martes, 3 de abril de 2012 20:08
  • 1-No, ya que no es una clave primaria, sino unica o indice unico.

    2-Si tienes razon, aunque ya habia confirmado mis sospechas

    3-Vaya... :P


    Vota mi respuesta si te ha sido útil. Gracias.

    martes, 3 de abril de 2012 20:17
  • Hola David,

    La verdad que mil perdones por entender que estabas modificando la PK, y solo por eso y en respuesta a tus necesidades acabo de escribir un post en tu honor, así que efectivamente como dices al primcipio ejecuta "Update,Delete,Insert" cuando debería de ser al reves "Delete,Update,Insert". Aunque si lo hiciese de esa forma en algún escenario se puede producir lo mismo.

    Te paso el link para que por lo menos lo leas:) 

    Entity Framework y los indices únicos 

    Saludos,


    phurtado
    Mi Blog Blog
    Sigueme en Twitter

    • Marcado como respuesta David Peláez jueves, 5 de abril de 2012 17:43
    martes, 3 de abril de 2012 22:38
  • Tras ver el ejemplo que pones en el artículo supongo que la lógica que utiliza el EF es la de realizar las operaciones contra la base de datos en el mismo orden que las insertamos en el contexto. Si es el programador es que las realiza no creo que tengamos muchos problemas. El problema de este caso es que es el usuario quien a través de sus acciones marca el orden de las consultas y claro, no siempre es el correcto (en términos de SQL).

    Dado que se trata del "clásico" aplicar / aceptar se podría añadir alguna lógica al programa del tipo "no se puede modificar un elemento si tiene pendiente algún borrado" o al revés, "no se puede borrar ningún elemento ni no se han aceptado todas la modificaciones". Estas comprobaciones son relativamente simples de llevar a cabo, pero estoy seguro que plantearán nuevos problemas, sobre todo de cara al usuario que tras aceptar los cambios esperará que al darle a cancelar se reviertan.


    Atentamente, Sergio.

    Blog
    Twitter

    miércoles, 4 de abril de 2012 6:17
  • El problema viene en que EF lanza las querys en el orden que ya todos sabemos (update-delete-insert), aunque yo en mi logica le mando el delete y el update en este orden. Con lo que estamos con un problema dificil de resolver.


    Vota mi respuesta si te ha sido útil. Gracias.

    miércoles, 4 de abril de 2012 7:08
  • Gracias por el soporte Pedro, y un gracias por el post. Mirare el tema del Merge que comentas haber si saco lago en claro.

    Vota mi respuesta si te ha sido útil. Gracias.

    miércoles, 4 de abril de 2012 7:09
  • Hola,

    Sergio, da lo mismo como lo hagas siempre es "update,delete,insert". Cuando realmente debería de ser "delete,update,insert", pero como comento en el post ni de esa forma te garantizas que funciones correcto, fijate en este código y la salida es la misma.

    using (Context ct = new Context())
    {
    
        
    
         ct.Paises.Add(new Pais() { Nombre = "La China", Codigo = "35" });
        
         var Francia = ct.Paises.Where(c => c.Nombre == "Francia").FirstOrDefault();
         var España = ct.Paises.Where(c => c.Nombre == "España").FirstOrDefault();
         ct.Paises.Remove(Francia);
         España.Codigo = "88";
        
        
         ct.SaveChanges();
    }


    David el Merge es una instrucción de T-SQL y hasta donde yo se no la implementa EF, con lo cual es complicado resolver el problema e incluso en el foro ya se han dado entradas interesantes con el tema de los indices únicos y lo único que te queda es confiar en la santa providencia o hacer las operaciones por separado y de forma individual, aún así sigo pensando lo mismo que con las pk que no se deberían de modificar.

    Mira estos link donde se trataron temas parecidos no con EF, pero a la postre el problema es el mismo.

    http://social.msdn.microsoft.com/Forums/es-ES/vcses/thread/abde6841-43ec-455e-a8c8-a54f56b9cb32
    http://social.msdn.microsoft.com/Forums/es-ES/vcses/thread/b6613de2-6f97-46d8-a713-ab11c0edae92
    http://social.msdn.microsoft.com/Forums/es-ES/vcses/thread/c6442f2c-b3bb-4883-84d0-37e01ae8a744

    yo lo que hacía hasta el momento es lo siguiente,partiendo de estos datos

    Id          Nombre                                             Codigo
    ----------- -------------------------------------------------- ------
    5           España                                             88
    7           La China                                           35

    declare @temp table 
    (
    	Codigo varchar(3)
    )
    insert into @temp(Codigo) values(35)
    
    update Pais
    set Codigo = '35'
    from @temp a left outer join Pais b
          on b.Codigo = a.Codigo
    where a.Codigo = 88 and b.Codigo is null

    En definitiva cuado estás en esa situación y con EF. Lo que deberías de hacer es

    1.Leer si existe.

    2.Modificar 

    3.Hacer un control de excepciones para controlar el error puesto que desde que lees hasta que modificas otro usuario puede haber modificado algo.

    3. Y sobre todo y lo más importante olvidarte de hacer la operación utilizando UoW, es decir primero un registro SaveChanges, y después lees y el otro.

    Saludos,



    phurtado
    Mi Blog Blog
    Sigueme en Twitter

    miércoles, 4 de abril de 2012 9:25
  • Esto es un simple comentario que no aporta nada, pero me he bajado la beta del EF 5.0.0 beta2 y el resultado obtenido es el mismo.

    "No se puede insertar una fila de clave duplicada en el objeto 'dbo.Pais' con el índice único 'IX_Pais'. El valor de clave duplicado es (33).\r\nSe terminó la instrucción."


    Atentamente, Sergio.

    Blog
    Twitter

    miércoles, 4 de abril de 2012 11:27