script net mvc migrations framework asp and c# asp.net-mvc-3 version-control entity-framework-4.3 ef-migrations

c# - net - migrations in entity framework



¿Cómo administrar migraciones en un proyecto con múltiples ramas? (7)

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 base de datos actualizada.

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

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

Actualizar

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ó nuevamente. Esto funcionaría para el dev-environment donde puede volcar la base de datos y volver a compilarla con todos los archivos de migración. 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 archivo de migración para actualizar la base de datos activa.


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

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


Creo que la respuesta aceptada es incorrecta. Existe una solución mucho mejor para manejar conflictos de fusión de marcos 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 migración en la rama de destino. Es decir, no rescaffold el código up / down, solo el estado en el archivo resx.

add-migration [the_migration_to_rescaffold_metadata_for]

Sin embargo, este procedimiento fallará si una migración diferente en la fusión ha cambiado la base de datos de tal forma que la migración ya no se puede ejecutar o si 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 menos no depender de otras tablas que no se modifiquen también en la migración. Problema muy leve pero a tener en cuenta.

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 migración A de la rama A cambia foo a booleano (0 se volverá falso automáticamente y> 0 se convertirá en verdadero)

  • La migración B de la rama B cambia a foo en cadena. Espera que sea un int pero es un booleano, la migración tendrá éxito. Se perderán datos ya que cuando se creó la migración 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 migración 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 migración arriba / abajo no depende de las cosas cambiadas por otra migración en la fusión, debería funcionar bien solo para actualizar los metadatos en las migraciones.


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 proceso de desarrollo, control de calidad y lanzamiento.

  • Rama de desarrollo - Desarrollo local
  • Rama principal: fusionar cambios desde la rama Desarrollo e implementar en mi entorno de ensayo (un sitio web Azure y una base de datos SQL)
  • Rama de publicación: fusionar cambios desde Principal e implementar en entorno de Producción (otro sitio web de Azure y base de datos 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 proceso 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 menos 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 proceso 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 modelo, 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 base de datos de producción (llámame paranoico).

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


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 base de datos de desarrollo, uso las migraciones para actualizar la base de datos de la manera más conveniente posible al agregar columnas, etc. a las tablas, agregar nuevas entidades, etc.

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

Para mí tiene muy poco sentido tratar de fusionar mis migraciones con otras migraciones, si todos tienen su propia base de datos y existe algún tipo de servidor de base de datos 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 propiedades 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 entero porque no eres ese brillante 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 basura) y recibes algunos cambios del repositorio 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 pruebas y verificas su código, y luego presionas hacia la central.

Pero, ¿y las migraciones? Bien, ahora sería el momento de hacer una migración moviendo el repositorio central, o la "prueba" de la rama más específicamente, contiene una migración pequeña y agradable desde su modelo a => modelo b. Esta migración será una y única migración, 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 absoluto, en mi opinión, deberíamos tener migraciones por rama o algo así.

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

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


La fusión de migraciones es una tarea manual en mi humilde opinión. Parte del código de migración 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 base de datos master a la versión utilizada antes de la bifurcación
  • Fusiona tus ramas
  • Excluya las clases de migración creadas después de la bifurcación desde el ensamblado fusionado
  • Agregue una nueva migración para la base de código fusionada que migrará su base de datos en el estado anterior a la bifurcación al estado después de fusionar las sucursales
  • Si sus clases de migración excluidas contienen alguna personalización, combínelas con la nueva clase de migración
  • Ejecute la migración para migrar su base de datos a la versión fusionada actual

Si sus ramas contienen múltiples pasos de migración (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 proceso de desarrollo en sí. 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 crea otra versión en una rama separada sin integración continua (= cambios de fusión continuos a la rama 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 base de datos. Luego puede habilitar las migraciones en el proyecto nuevamente y agregar la migración inicial para usar su base de datos actual como punto de partida = no hay manera de volver a la versión anterior porque no existirá información sobre migraciones anteriores.


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 modelo 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 ramas, porque el problema general es fusionar los cambios de migración realizados al mismo tiempo, pero teniendo efectivamente un estado fuente diferente del modelo.

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 comando UpdateDatabase del desarrollador B aún 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 crear 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 hace referencia la migración en el archivo resx dentro del archivo de migración.

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


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 rama actual; de lo contrario, deberá volver a realizar todas sus migraciones y resolver los conflictos de cambio del modelo de datos a mano.

  1. restaurar su base de datos de desarrollo con datos de entorno en vivo
  2. ejecute update-database , debería ejecutar las migraciones de su sucursal y quejarse de que "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 nuevamente
  5. impulsa tus cambios

La magia en el paso 3 básicamente le dice a EF que se calle sobre los modelos 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 migración desplegada.

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

  2. Fusionar ramas en git

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

  4. Eliminar su migración local (MiMigración)

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

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

  7. Ejecutar: ACTUALIZAR-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á a todas en una sola.

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