¿Cómo administrar migraciones en un proyecto con múltiples twigs?

Tengo un proyecto ASP.NET MVC3 que usa Entity Framework 4.3 con el enfoque de primer código. Yo uso Migraciones para mantener la database actualizada.

El proyecto está bajo control de fuente y tengo varias sucursales. Lo que acabo de darme count es que habrá un problema cuando quiera fusionar una de mis twigs en el maestro. Como he creado files de migration en ambas twigs, habrá migraciones superpuestas cuando me fusione, lo que probablemente genere conflictos.

¿Hay una buena manera de administrar migraciones en un proyecto con múltiples twigs?

Actualizar

Una forma sería fusionar, luego eliminar todos los files de migration creados mientras las twigs estaban separadas, y luego crear un nuevo file de migration que contenga todos los cambios desde el momento en que se creó la twig hasta que se fusionó nuevamente. Esto funcionaría para el dev-environment donde puede volcar la database y volver a comstackrla con todos los files de migration. El problema entonces sería el ambiente en vivo. Como no puede retroceder al momento en que se creó la sucursal sin el riesgo de perder datos, habrá un conflicto cuando intente usar su nuevo file de migration para actualizar la database activa.

Creo que la respuesta aceptada es incorrecta. Existe una solución mucho mejor para manejar conflictos de fusión de frameworks de entidades en una pregunta similar.

Todo lo que necesita hacer después de una fusión es volver a andamiar los metadatos de la migration en la twig de destino. Es decir, no rescaffold el código up / down, solo el estado en el file resx.

add-migration [the_migration_to_rescaffold_metadata_for] 

Sin embargo, este procedimiento fallará si una migration diferente en la fusión ha cambiado la database de tal forma que la migration ya no se puede ejecutar o si no da un resultado inesperado. Dicho eso, creo que es un caso muy raro, ya que la mayoría de las migraciones deben generarse automáticamente, o al less no depender de otras tablas que no se modifiquen también en la migration. Problema muy leve pero a tener en count.

Uno de estos casos podría ser fxp (no podría pensar en un mejor ejemplo)

  • Columna foo es un int y las filas contienen [0, 1, 2]

  • La migration A de la twig A cambia foo a boolean (0 se volverá falso automáticamente y> 0 se convertirá en verdadero)

  • La migration B de la twig B cambia a foo en cadena. Espera que sea un int pero es un boolean, la migration tendrá éxito. Se perderán datos ya que cuando se creó la migration B, las filas contendrían ["0", "1", "2"]. Cuando migra una columna alterada a booleana (y lo hizo con éxito y con el resultado esperado) las filas ahora contendrán ["0", "1", "1"] y la migration B tendrá un resultado final diferente al observado en Rama B.

Probablemente haya más casos extremos en los que las cosas podrían salir mal con la solución. Pero si el código de migration arriba / abajo no depende de las cosas cambiadas por otra migration en la fusión, debería funcionar bien solo para actualizar los metadatos en las migraciones.

La fusión de migraciones es una tarea manual en mi humilde opinión. Parte del código de migration se genera automáticamente y, por lo general, no fusionamos el código generado automáticamente; en su lugar, ejecutamos la autogeneración luego de la fusión.

Hasta que el equipo ADO.NET proporcione alguna recomendación, seguiré un principio simple:

  • Antes de hacer la fusión, revertir la database master a la versión utilizada antes de la bifurcación
  • Fusiona tus twigs
  • Excluya las classs de migration creadas después de la bifurcación desde el ensamblado fusionado
  • Agregue una nueva migration para la base de código fusionada que migrará su database en el estado anterior a la bifurcación al estado después de fusionar las sucursales
  • Si sus classs de migration excluidas contienen alguna personalización, combínelas con la nueva class de migration
  • Ejecute la migration para migrar su database a la versión fusionada actual

Si sus twigs contienen múltiples pasos de migration (versión), los perderá y terminará con dos versiones, antes de la bifurcación y después de la fusión.

Editar:

No funcionará en el entorno en vivo. El problema aquí sería el process de desarrollo en sí. Si tiene un entorno en vivo, debe mantener su twig intacta (excepto correcciones de errores menores). Si continúa el desarrollo en esa twig con implementación de producción y al mismo time crea otra versión en una twig separada sin continuous integration (= cambios de fusión continuos a la twig principal para integrar su nuevo desarrollo con la base de código principal), tiene una gran problema. Creo que las migraciones en general no pueden manejar esto.

La única opción en ese caso sería eliminar todas las migraciones de la solución fusionada y eliminar la tabla MigrationHistory de la database. Luego puede habilitar las migraciones en el proyecto nuevamente y agregar la migration inicial para usar su database actual como punto de partida = no hay manera de volver a la versión anterior porque no existirá información sobre migraciones anteriores.

Editar: un colega mío descubrió que era más fácil hacer esto, dejé mi respuesta original en la parte inferior para que estuviera completa.

(MUY IMPORTANTE) las migraciones en un entorno en vivo no deben entrar en conflicto con las de su twig actual; de lo contrario, deberá volver a realizar todas sus migraciones y resolver los conflictos de cambio del model de datos a mano.

  1. restaurar su database de desarrollo con datos de entorno en vivo
  2. ejecuta update-database , debe ejecutar las migraciones de tu sucursal y quejarse de que "no se puede actualizar la database para que coincida con el model actual, bla, bla …"
  3. ejecutar add-migration MergeBranchBToMaster -ignoreChanges , esto creará una migration vacía.
  4. ejecutar update-database nuevamente
  5. impulsa tus cambios

La magia en el paso 3 básicamente le dice a EF que se calle sobre los models que no coinciden, por lo tanto, asegúrese de que sus migraciones no entren en conflicto con las que se encuentran en el entorno en vivo. Si lo hacen, siempre puedes crear scripts SQL para impulsar las migraciones faltantes (que en realidad es el método preferido).

Respuesta original

He encontrado una solución bastante directa basada en la respuesta de @Ladislav Mrnka. Esto funcionará con el entorno en vivo [1], solo debe tener cuidado de no cambiar ninguna migration desplegada.

  1. Antes de combinar, tome nota de la migration que agregó (MyMigration) y su migration anterior (BaseMigration)

  2. Fusionar twigs en git

  3. Abra la console de Package Manager y ejecute: UPDATE-DATABASE -TargetMigration: BaseMigration. Esto revertirá su database al estado antes de aplicar cualquiera de las migraciones conflictivas

  4. Eliminar su migration local (MiMigración)

  5. Ejecutar: ACTUALIZAR-BASE DE DATOS. Esto aplicará todas las migraciones más recientes hechas en otras twigs.

  6. Ejecutar: ADD-MIGRATION MyMigration. Esto volverá a generar su migration local en function del estado actual de la database, como git -rebase.

  7. Ejecutar: ACTUALIZAR-BASE DE DATOS. Actualice la database con su migration local.

Esto también funciona si tiene múltiples migraciones locales, pero las fusionará a todas en una sola.

[1] al trabajar con un entorno en vivo, me refiero a que la migration generada se puede aplicar al entorno en vivo que ya puede tener aplicadas algunas / todas las migraciones de las otras twigs. Los pasos en sí son puramente para fines de desarrollo.

Rowan Miller ha realizado un gran video sobre este tema en el canal 9: Migraciones – Entornos de equipo . Se refiere al marco de la entidad 6.

Describe un escenario donde los primeros desarrolladores A y B están trabajando en el mismo model y A revisa primero. Ahora el desarrollador B tiene que lidiar con los problemas que tiene cuando obtiene la última versión de A.

Esto es esencialmente lo mismo que tener conflictos entre diferentes twigs, porque el problema general es fusionar los cambios de migration realizados al mismo time, pero teniendo efectivamente un estado fuente diferente del model.

La solucion es:

  • Al resolver los conflictos del sistema de control de versiones, el desarrollador B debe aceptar ambos cambios de él mismo y del desarrollador A.
  • Un command UpdateDatabase del desarrollador B aún fallaría en este momento (Mensaje de error: "No se puede actualizar la database para que coincida con el model actual porque hay cambios pendientes …" )
  • El desarrollador B tiene que crear una "migration vacía" usando la opción IgnoreChanges :

Add-Migration NameOfMigration -IgnoreChanges

Entonces el command UpdateDatabase tendrá éxito.

Fuente del problema

El origen del error que ocurre al actualizar la database se debe a que EF almacena una instantánea del model al que hace reference la migration en el file resx dentro del file de migration.

En este caso, la instantánea B de los desarrolladores del "model actual" no es correcta después de get / fusionar los cambios realizados por el desarrollador A.

He pensado un poco en esto y espero contribuir con las diferentes opiniones y prácticas presentadas aquí.

Considera lo que representan tus migraciones locales. Cuando trabajo localmente con una database de desarrollo, uso las migraciones para actualizar la database de la manera más conveniente posible al agregar columnas, etc. a las tablas, agregar nuevas entidades, etc.

Entonces, Add-Migration verifica mi model actual (llamémoslo model b) a mi model anterior (model a) y genera una migration para pasar de a => b en la database.

Para mí tiene muy poco sentido tratar de fusionar mis migraciones con otras migraciones, si todos tienen su propia database y existe algún tipo de server de database stage / test / dev / production en la organización. Todo esto depende de cómo lo haya configurado el equipo, pero tiene sentido aislarse mutuamente de los cambios que otras personas realizan si realmente quiere trabajar de forma distribuida.

Bueno, si trabajas distribuido y tienes alguna entidad, Persona, por ejemplo, en la que trabajas. Por alguna razón, muchas otras personas también están trabajando en ello. Entonces, agregas y eliminas properties en Person según sea necesario para tu historia en particular en el sprint (todos estamos trabajando ágilmente aquí, ¿verdad?), Como el número de Seguridad Social que creaste por primera vez en un integer porque no eres ese shiny y luego a una cadena, etc.

Agrega FirstName y LastName.

Luego terminaste y tienes diez migraciones extrañas hacia arriba y hacia abajo (es probable que hayas eliminado algunas de ellas mientras trabajabas porque eran pura mierda) y recibes algunos cambios del repository central de Git. Guau. Su colega Bob también necesitaba algunos nombres, ¿quizás debería haber hablado el uno con el otro?

De todos modos, él ha agregado NameFirst y NameLast, supongo … entonces, ¿qué haces? Bueno, te fusionas, refactorizas, cambias para que tenga nombres más sanos … como FirstName y LastName, ejecutas tus testings y verificas su código, y luego presionas hacia la central.

Pero, ¿y las migraciones? Bien, ahora sería el momento de hacer una migration moviendo el repository central, o la "testing" de la twig más específicamente, contiene una migration pequeña y agradable desde su model a => model b. Esta migration será una y única migration, no diez extrañas.

¿Ves a lo que me refiero? Estamos trabajando con pequeños y bonitos pocos y las comparaciones de ellos constituyen las migraciones reales. Entonces, no debemos fusionar las migraciones, en mi opinión, deberíamos tener migraciones por twig o algo así.

De hecho, ¿necesitamos siquiera crear la migration en la twig después de la fusión? Sí, si esta database se actualiza automáticamente, debemos hacerlo.

Tengo que trabajar un poco más, esos son mis pensamientos sobre esto, al less.

Considere usar una biblioteca de migration diferente que no cause estos conflictos, como FluentMigrator o Migrator.NET.

No creo que las migraciones de EF estén realmente lists para el uso general con sucursales y fusiones, es mucho trabajo y demasiado fácil cometer errores desagradables.

Creo que lo que dice @LavaEater tiene mucho sentido. Estoy implementando una estrategia de ramificación (desarrollo, principal, versión) y alineándola con los entornos en el process de desarrollo, control de calidad y lanzamiento.

  • Rama de desarrollo – Desarrollo local
  • Rama principal: fusionar cambios desde la twig Desarrollo e implementar en mi entorno de ensayo (un website Azure y una database SQL)
  • Rama de publicación: fusionar cambios desde Principal e implementar en entorno de Producción (otro website de Azure y database SQL)

Me he encontrado con el problema mencionado anteriormente y, en mi opinión, las complicaciones relacionadas con las migraciones y las posibles soluciones introducen un gran riesgo en el process de publicación. Ejecutar migraciones independientes en Desarrollo, Principal y Liberar de manera efectiva significa que el esquema que incluí en la construcción en Dev no es el esquema que entra en QA en Etapa y el esquema que QA firma en Etapa no es el esquema que se implementa en Live ( a less que siga una de las soluciones sugeridas que estoy seguro funcionaría pero puede ser propenso a errores).

Hacer eco de @LavaEater: ¿cuál es el beneficio real que obtengo primero del código EF? Personalmente, creo que es la facilidad con la que puedo generar un esquema a partir del código (y posiblemente modificar las migraciones generadas automáticamente si quiero). Después de eso, las migraciones son una complicación de lo que debería ser un simple process de implementación.

Mi pensamiento actual es usar código primero para generar las migraciones en desarrollo y luego:

  • Opción A) – Use Update-Database -script para guiar los cambios de esquema y ponerlos bajo control de fuente. Todavía existe cierto potencial de conflictos si 2 personas están modificando el mismo model, pero creo que es más fácil de administrar.

  • Opción B) – Use algo como SQL Compare para generar scripts de cambio de esquema. Esto es potencialmente más flexible y transparente ya que me gustaría ver exactamente qué cambios de esquema estoy aplicando a mi database de producción (llámame paranoico).

¿Me estoy perdiendo de algo? Imagino que habrá que hacer alguna configuration para deshabilitar las primeras migraciones de código en las twigs Principal y Release (en el supuesto de que la database se creará y actualizará mediante scripts). Aparte de eso, se siente como una solución segura, pero yo valoraría una segunda opinión.