¿Cómo gestionar migraciones en un proyecto con múltiples ramas?


Tengo un ASP.NET Proyecto MVC3 que utiliza Entity Framework 4.3 con el enfoque code-first. Utilizo migraciones para mantener la base de datos actualizada.

El proyecto está bajo control de código fuente y tengo varias ramas. Lo que acabo de darme cuenta es que habrá un problema cuando quiera fusionar una de mis ramas en el maestro. Dado que he creado archivos de migración en ambas ramas, habrá migraciones superpuestas cuando combine, lo que probablemente causará conflictos.

Es hay una buena manera de gestionar las Migraciones en un proyecto con múltiples ramas?

Update

Una forma sería fusionar, luego eliminar todos los archivos de migración creados mientras las ramas estaban separadas, y luego crear un nuevo archivo de migración que contenga todos los cambios desde el momento en que se creó la rama hasta que se fusionó de nuevo. Esto funcionaría para el entorno de desarrollo donde puede volcar la base de datos y volver a construirla con todos los archivos de migración. El problema entonces sería el ambiente vivo. Dado que no pudo retroceder al momento en que se creó la rama sin el riesgo de perder datos, habrá un conflicto cuando intente usar su nuevo archivo de migración para actualizar la base de datos en vivo.

Author: Shaul Behr, 2012-05-11

7 answers

Creo que la respuesta aceptada es incorrecta. Existe una solución mucho mejor para manejar los conflictos de fusión de migración de entity framework en una pregunta similar.

Todo lo que necesita hacer después de una fusión es re-andamiaje de los metadatos de la migración en la rama de destino. Es decir, no re-plegar el código arriba / abajo, solo el estado en el archivo resx.

add-migration [the_migration_to_rescaffold_metadata_for]

Este procedimiento fallará si una migración diferente en la fusión ha cambiado la base de datos de tal manera que la migración ya no se puede ejecutar o da un resultado inesperado. Dicho esto, creo que es un caso muy raro, ya que la mayoría de las migraciones deben generarse automáticamente, o al menos no depender de otras tablas que no se cambian en la migración en sí también. Un problema muy pequeño, pero que hay que tener en cuenta.

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

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

  • Migración A desde branch A change foo to boolean (0 se convertirá en false automáticamente y > 0 se hará realidad)

  • Migración B desde la rama B cambia foo a string. Se espera que sea un int pero es un booleano, la migración tendrá éxito sin embargo. Los datos se perderán ya que cuando se creó la migración B, las filas contendrían ["0", "1", "2"]. Cuando la migración de una columna alterada a booleano (y lo hizo con éxito y con el resultado esperado) las filas ahora contendrán ["0", "1", "1"] en cambio, y la Migraciónb tendrá un resultado final diferente al observado en la rama B.

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

 16
Author: oldwizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:10

Fusionar migraciones es una tarea manual de IMHO. Parte del código de migración se genera automáticamente y, por lo general, no fusionamos el código generado automáticamente, sino que ejecutamos la autogeneración nuevamente después de la fusión.

Hasta ADO.NET el equipo proporciona alguna recomendación que seguiría un principio simple:

  • Antes de realizar la fusión, revierta la base de datos maestra a la versión utilizada antes de la ramificación
  • Fusiona tus ramas
  • Excluir clases de migración creadas después de ramificar desde fusionadas asamblea
  • Agregue una nueva migración para merged code base que migrará su base de datos en el estado anterior a la ramificación al estado después de fusionar ramas
  • Si sus clases de migración excluidas contienen alguna personalización, combínelas con la nueva clase de migración
  • Ejecute migration para migrar su base de datos a la versión fusionada actual

Si sus ramas contenían varios pasos de migración (versión), los perderá y terminará con dos versiones, antes de ramificación y después de la fusión.

Editar:

No funcionará en un entorno en vivo. El problema aquí sería el propio proceso de desarrollo. Si tiene un entorno en vivo, debe mantener su rama intacta (excepto correcciones de errores menores). Si continúa el desarrollo en esa rama con implementación de producción y al mismo tiempo compila otra versión en una rama separada sin integración continua (=la fusión continua cambia a la rama principal para integrar su nueva desarrollo con la base de código principal) tiene un gran problema. Creo que las migraciones en general no pueden manejar esto.

La única opción en tal caso sería probablemente eliminar todas las migraciones de la solución combinada y eliminar la tabla MigrationHistory de la base de datos. Entonces puede habilitar las migraciones en el proyecto de nuevo y agregar la migración inicial para usar su base de datos actual como punto de partida = no hay forma de volver a la versión anterior porque no existirá información sobre las migraciones anteriores.

 13
Author: Ladislav Mrnka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-12 12:46:22

Edit: un colega mío descubrió una manera más fácil de hacer esto, dejé mi respuesta original en la parte inferior para completar.

(MUY IMPORTANTE) las migraciones en un entorno en vivo no deben entrar en conflicto con las de su rama actual, de lo contrario, debe rehacer todas sus migraciones y resolver los conflictos de cambio del modelo de datos a mano.

  1. restaure su base de datos de desarrollo con datos de entorno en vivo
  2. run update-database, debe ejecutar migraciones desde su rama, y se quejan de ' no se puede actualizar la base de datos para que coincida con el modelo actual bla bla..'
  3. ejecutar add-migration MergeBranchBToMaster -ignoreChanges, esto creará una migración vacía.
  4. ejecutar update-database de nuevo
  5. empuje sus cambios

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

Respuesta original

He encontrado una solución bastante sencilla basada en la respuesta de @Ladislav Mrnka. Esto funcionará con live environment[1], solo hay que tener cuidado de no cambiar ninguna migración implementada.

  1. Antes de fusionar, toma nota de la migración que agregaste (MyMigration), y su migración previa (BaseMigration)

  2. Combinar ramas en git

  3. Abra la consola del Administrador de paquetes y ejecute: UPDATE-DATABASE-TargetMigration: BaseMigration. Esto revertirá su base de datos al estado antes de que se aplique cualquiera de las migraciones en conflicto

  4. Eliminar su migración local (MyMigration)

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

  6. Ejecutar: ADD-MIGRATION MyMigration. Esto volverá a generar su migración local basada en el estado actual de la base de datos, como git-rebase.

  7. Ejecutar: ACTUALIZACIÓN-BASE DE DATOS. Actualice la base de datos con su migración local.

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

[1] al trabajar con live environment, quiero decir que la migración generada se puede aplicar a live environment que ya puede tener algunas/todas las migraciones de las otras ramas aplicadas. Los pasos en sí son puramente para fines de desarrollo.

 13
Author: Bill Yang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-27 13:23:32

Rowan Miller ha hecho un gran video sobre este tema en el canal 9: Migraciones - Entornos de equipo. Se refiere a entity framework 6.

Describe un escenario donde el primer desarrollador A y B están trabajando en el mismo modelo y A comprueba 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 ramas, porque el problema general es fusionar la migración los cambios lo hicieron al mismo tiempo pero efectivamente tienen un estado de origen diferente del modelo.

La solución es:

  • Al resolver los conflictos del sistema de control de versiones, el desarrollador B tiene que aceptar ambos cambios de sí mismo y desarrollador A.
  • Un comando UpdateDatabase del desarrollador B todavía fallaría en este momento (Mensaje de error: "No se puede actualizar la base de datos para que coincida con el modelo actual porque hay cambios pendientes...")
  • El desarrollador B tiene que cree una "migración vacía" usando la opción IgnoreChanges:

Add-Migration NameOfMigration -IgnoreChanges

Entonces el comando UpdateDatabase tendrá éxito.


Fuente del problema

El origen del error que ocurre al actualizar la base de datos se debe a que EF almacena una instantánea del modelo al que se refiere una migración en el archivo resx dentro del archivo de migración.

En este caso desarrolladores B instantánea del "modelo actual" no es correcta después de obtener / fusionar los cambios realizados por desarrollador A.

 8
Author: Martin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-19 09:16:45

He reflexionado sobre esto y espero contribuir a las diferentes opiniones y prácticas aquí presentadas.

Considere lo que realmente representan sus migraciones locales. Cuando trabajo localmente con una base de datos de desarrollo, utilizo migraciones para actualizar la base de datos de la manera más conveniente posible al agregar columnas, etc. a tablas, agregar nuevas entidades, etc.

Entonces, Add-Migration comprueba mi modelo actual (llamémoslo modelo b) contra mi modelo anterior (modelo a) y genera una migración para ir desde a => b en la base de datos.

Para mí tiene muy poco sentido tratar de fusionar mis migraciones con cualquier migración de elses, si todo el mundo tiene su propia base de datos y entonces existe algún tipo de servidores de base de datos de etapa / prueba / dev / producción en la organización. Todo esto depende de cómo el equipo lo ha configurado, pero tiene sentido aislarse unos a otros de los cambios que otras personas hacen si realmente quieres trabajar una manera 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. Por lo tanto, agrega y elimina propiedades en Person según sea necesario para su historia en particular en el sprint (todos estamos trabajando ágiles aquí, ¿no?), como el número de Seguro Social que primero convertiste en un entero porque no eres tan brillante y luego en una cadena, etc.

Usted agrega FirstName Y Apellido.

Entonces has terminado y tienes diez migraciones hacia arriba y hacia abajo extrañas (probablemente eliminaste algunas de ellas mientras trabajabas ya que eran solo basura) y consigues algunos cambios del repositorio central de Git. Wow. Su colega Bob también necesitaba algunos nombres, ¿tal vez deberían haber hablado el uno con el otro?

De todos modos, ha añadido NameFirst y NameLast, supongo... entonces, ¿a qué te dedicas? Bueno, se fusiona, refactoriza, cambia para que tenga más nombres cuerdos... al igual que FirstName y LastName, se ejecuta su pruebas y compruebas su código, y luego empujas a la central.

Pero ¿qué pasa con las migraciones? Bueno, ahora sería el momento de hacer una migración moviendo el repositorio central, o la rama "prueba" más específicamente, contiene una pequeña migración desde su modelo a => modelo b. Esta migración será una y solo una migración, no diez extrañas.

¿Ves lo que quiero decir? Estamos trabajando con nice little pocos y las comparaciones de ellos constituyen la real migración. Por lo tanto, no deberíamos fusionar migraciones en absoluto, en mi opinión, deberíamos tener migraciones por rama o algo así.

De hecho, ¿necesitamos crear la migración en la rama después de fusionar? Sí, si esta base de datos se actualiza automáticamente, lo necesitamos.

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

 4
Author: LavaEater,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-12 09:54:03

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

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

 2
Author: Eamon Nerbonne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-19 13:51:13

Creo que lo que @LavaEater está diciendo tiene mucho sentido. Estoy implementando una estrategia de ramificación (Desarrollo, Main, Release) y alineándola con los entornos en el proceso de desarrollo, QA y release.

  • Subdivisión de desarrollo - Desarrollo local
  • Main branch-Combinar los cambios de la rama de desarrollo y desplegar en mi entorno de ensayo (un sitio web de Azure y base de datos SQL)
  • Release branch-Combinar los cambios de Main y deploy al entorno de producción (otro sitio web de Azure y base de datos SQL)

Me he encontrado con el problema discutido anteriormente y en mi opinión las complicaciones en torno a las migraciones y las posibles soluciones introducen una gran cantidad de riesgo en el proceso de liberación. Ejecutar migraciones independientes en Desarrollo, Main y Release significa efectivamente que el esquema que incluí en la compilación en Dev no es el esquema que entra en QA en la etapa y el esquema que QA firma en la etapa no es el esquema que es implementado para vivir (a menos que siga una de las soluciones sugeridas que estoy seguro que funcionaría, pero puede ser propenso a errores).

Para echo @LavaEater - ¿cuál es el beneficio real que obtengo de EF code first? Personalmente, creo que es la facilidad con la que puedo generar un esquema a partir de código (y potencialmente ajustar las migraciones generadas automáticamente si quiero). Después de eso, las migraciones son una complicación de lo que debería ser un proceso de implementación simple.

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

  • Opción A)-Use Update-Database-script para programar los cambios de esquema y ponerlos bajo control de código fuente. Todavía hay un cierto potencial de conflictos si 2 personas están modificando el mismo modelo, pero creo que es más fácil de manejar.

  • Opción B) - Utilice algo como SQL Compare para generar scripts de cambio de esquema. Esto es potencialmente más flexible y transparente, ya que me gusta ver exactamente qué esquema cambios que estoy aplicando a mi base de datos de Producción (llámame paranoid).

¿Me estoy perdiendo algo? Imagino que habrá alguna configuración que hacer para deshabilitar las primeras migraciones de código en las ramas Main y Release (asumiendo que la base de datos será creada y actualizada por scripts). Aparte de eso, se siente como una solución segura, pero valoraría una segunda opinión.

 0
Author: Liam Weston,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-08 14:36:11