update the script migrations framework first create consider code c# ef-code-first sql-server-ce-4 code-first-migrations

c# - the - ef migrations to sql



Código de primeras migraciones y error de inicialización. (3)

He jugado un poco con el código que proporcionó y en este caso (con SQL Server en lugar de CE) y he llegado a lo siguiente. He eliminado el código de Database.Create y he permitido que las migraciones automáticas de EF lo hagan. Esto se ejecuta y llama al método Seed correctamente ahora.

internal class Program { private static void Main() { EntityFrameworkProfiler.Initialize(); Database.DefaultConnectionFactory = new SqlConnectionFactory("System.Data.SqlServer"); Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyContextConfiguration>()); using (var context = new MyContext()) { var element = context.Dummies.FirstOrDefault(); } } } internal class Dummy { public String Id { get; set; } } internal sealed class MyContext : DbContext { public MyContext() : base(@"Data Source=localhost;Initial Catalog=Dummies;User Id=<USER_ID>;Password=<PASSWORD>;MultipleActiveResultSets=False;") { } public DbSet<Dummy> Dummies { get; set; } } internal sealed class MyContextConfiguration : DbMigrationsConfiguration<MyContext> { public MyContextConfiguration() { AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = true; } protected override void Seed(MyContext context) { context.Dummies.AddOrUpdate(new Dummy() { Id = "First" }); } }

Si busca en el analizador de EF, verá que ahora se ejecutan más consultas en la base de datos (e incluso una comprobación de la antigua tabla EdmMetaData ... lo cual es muy extraño, ya que debería eliminar esa tabla si se encuentra ahora a favor de la tabla __MigrationHistory). No sé por qué sucede esto, supongo que es un problema de configuración de nuestro lado (del cual todavía no sé cómo solucionarlo) o es un error en el código de migración.

Por lo tanto, creo que con las migraciones de EF nos dejamos a las migraciones basadas en código ( consulte la publicación de mi blog aquí ) o las migraciones automáticas (como lo demuestra este fragmento de código). Supongo que a medida que pase el tiempo entenderé mejor por qué EF (o la forma en que migro) tiene este extraño comportamiento, o EF mejorará a medida que evolucione.

No estoy seguro de cómo usar la función de migración del código primero. A mi entender, debería crear mi base de datos si aún no existe, y actualizarla al esquema más reciente según los archivos de migración. Pero estoy luchando con eso, porque siempre recibo muchos errores y no estoy seguro en general de cómo usar esto correctamente.

internal class Program { private static void Main() { EntityFrameworkProfiler.Initialize(); Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0"); Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Migrations.Configuration>()); using (var context = new MyContext()) { var exists = context.Database.Exists(); if (!exists) { context.Database.Create(); } var element = context.Dummies.FirstOrDefault(); } } } public class MyContext : DbContext { public MyContext() : base(string.Format(@"DataSource=""{0}""", @"C:/Users/user/Desktop/MyContext.sdf")) { } public DbSet<Dummy> Dummies { get; set; } } internal sealed class Configuration : DbMigrationsConfiguration<MyContext> { public Configuration() { AutomaticMigrationsEnabled = true; } protected override void Seed(CodeFirstTest.MyContext context) { } }

Usando el Perfil de Entity Framework, verifico qué declaraciones se ejecutan. Cuando ejecuto el programa sin una base de datos existente, obtengo el siguiente resultado:

- instrucción # 1 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- instrucción # 2 WARN: System.Data.SqlServerCe.SqlCeException (0x80004005): la tabla especificada no existe. [__MigrationHistory] en System.Data.SqlServerCe.SqlCeCommand.ProcessResults (Int32 hr) en System.Data.SqlServerCe. System.Data.SqlServerCe.SqlCeCommand.Execute .P.P.SqlCr.nq.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png. ProfiledCommand.ExecuteDbDataReader (Comportamiento CommandBehavior)

- instrucción # 3 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- instrucción # 4 WARN: System.Data.SqlServerCe.SqlCeException (0x80004005): la tabla especificada no existe. [__MigrationHistory] en System.Data.SqlServerCe.SqlCeCommand.ProcessResults (Int32 hr) en System.Data.SqlServerCe. System.Data.SqlServerCe.SqlCeCommand.Execute .P.P.SqlCr.nq.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png. ProfiledCommand.ExecuteDbDataReader (Comportamiento CommandBehavior)

- instrucción # 5 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- instrucción # 6 WARN: System.Data.SqlServerCe.SqlCeException (0x80004005): la tabla especificada no existe. [__MigrationHistory] en System.Data.SqlServerCe.SqlCeCommand.ProcessResults (Int32 hr) en System.Data.SqlServerCe. System.Data.SqlServerCe.SqlCeCommand.Execute .P.P.SqlCr.nq.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png. ProfiledCommand.ExecuteDbDataReader (Comportamiento CommandBehavior)

- instrucción # 7 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- declaración # 8 WARN: System.Data.SqlServerCe.SqlCeException (0x80004005): la tabla especificada no existe. [__MigrationHistory] en System.Data.SqlServerCe.SqlCeCommand.ProcessResults (Int32 hr) en System.Data.SqlServerCe. System.Data.SqlServerCe.SqlCeCommand.Execute .P.P.SqlCr.nq.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png.png. ProfiledCommand.ExecuteDbDataReader (Comportamiento CommandBehavior)

- declaración # 9 comenzar la transacción con nivel de aislamiento: Serializable

- declaración # 10 CREAR TABLA [Dummies] ([Nombre] nvarchar NOT NULL, CONSTRAINT [PK_Dummies] CLAVE PRIMARIA ([Nombre]))

- declaración # 11 CREATE TABLE [ MigrationHistory] ([MigrationId] nvarchar NOT NULL, [CreatedOn] [datetime] NOT NULL, [Model] [image] NOT NULL, [ProductVersion] nvarchar NOT NULL, CONSTRAINT [PK _MigrationHistory] CLAVE PRINCIPAL ([MigrationId]))

- instrucción # 12 INTRODUZCA EN [__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES (''201207261524579_InitialCreate'', ''2012-07-26T15: 24: 58.523'', 0x1F8B080, ''2012-07-26T15: 24: 58.523'', 0x1F8B080, ''2012-07-26T15: 24: 58.523'', 0x1F8B080, '' 1 '')

- declaración # 13 compromiso transacción

- declaración # 14 SELECCIONE ARRIBA (1) [c]. [Nombre] COMO [Nombre] DE [Dummies] AS [c]

Como puede ver, está intentando acceder a la base de datos cuatro veces antes de que realmente cree la base de datos. Esto no parece correcto. Cuando inicio la aplicación con una base de datos existente, consultará la base de datos 7 veces antes de que se ejecute cualquiera de mis consultas reales. Tenga en cuenta que esto sucede con context.Database.Create() , no con .Exists() .

Además, nunca se llama al método de inicialización de mi configuración, pero sí al constructor.

Todo esto parece muy equivocado y confuso. Espero que alguien pueda aclararme por qué los errores ocurren tan a menudo al principio, y por qué mi método de semilla no se llama en absoluto.

Estoy usando las últimas versiones estables de SqlServer compact y Entity Framework.

package id = "EntityFramework" version = "4.3.1" targetFramework = "net40"

package id = "Microsoft.SqlServer.Compact" version = "4.0.8854.2" targetFramework = "net40"


Parece que hay muchas formas de configurar el marco de la entidad y cada uno tiene su propia opinión sobre lo que es mejor. Todo lo que puedo ofrecer es mi opinión basada en lo que hemos estandarizado en mi trabajo. Mucho de esto es la preferencia del desarrollador. Resulta que mi preferencia es controlar lo más posible, por lo que siempre entiendo exactamente qué está sucediendo y cuándo.

Migraciones automáticas

En primer lugar, si bien las Migraciones automáticas pueden ser convenientes, pero causan muchos problemas, especialmente a medida que un proyecto crece y / o los datos se vuelven más complejos. En mi opinión, cualquier sistema comercial / de producción debería tener más control que este. Siempre desactivamos las migraciones automáticas para todos nuestros proyectos principales estableciendo AutomaticMigrationsEnabled = false; . Ejecutamos nuestras migraciones explícitamente cuando queremos que se realicen (en dev, esto está en la consola del administrador de paquetes en visual studio escribiendo Update-Database y en producción, hemos escrito nuestra pequeña utilidad de migración que simplemente llama a migrar al último código explícitamente, pero ninguno son automáticos).

¡La respuesta de @ Terric me asusta con las migraciones automáticas Y con la pérdida de datos permitida! No quiero ser el tipo que implementa una solución y borra algunos datos importantes debido a una alteración de columna mal ejecutada que resultó en la pérdida de datos. Como nota al margen cuando agotamos la migración explícitamente en dev, a menudo uso el interruptor -v para una salida detallada ( Update-Database -v ). Esto le permite ver el SQL que se está ejecutando y cualquier falla / advertencia si es apropiado.

También ha sido nuestra experiencia que cambiar estas configuraciones después de que haya varias migraciones en desarrollo no va bien. No estoy seguro de dónde se está realizando el seguimiento de esto, pero comenzar un proyecto nuevo con las migraciones automáticas desactivadas garantiza que no ocurra nada inesperado.

Personalmente, eliminaría el Inicializador que tiene MigrateDatabaseToLatestVersion y ejecutaría el migrador exactamente cuando lo deseo (ya sea a través de la consola del administrador de paquetes o mediante mi propio código explícito en alguna parte).

Creando una base de datos si no existe

Este comportamiento lo proporciona un DatabaseInitializer (no realmente EntityFramework). El inicializador CreateDatabaseIfNotExists está integrado en EntityFramework y un valor predeterminado en algunas versiones. Sin embargo, otra vez no soy uno para todo el comportamiento inferido de la aplicación. En mi opinión me gustaría un poco más de control.

Este tipo tiene un ejemplo de un inicializador de base de datos personalizado heredado de CreateDatabaseIfNotExists integrado . Pero siempre puede crear su propio e implementar la lógica exacta que desee ver (incluida la creación de su base de datos). De nuevo, esto solo evita el comportamiento inesperado. Mi preferencia personal como desarrollador es controlar esto estrechamente a menos que solo esté haciendo el tonto con una maqueta o un proyecto de prueba.

DatabaseInitializer personalizado super simple sin comportamiento inesperado:

namespace MyProject.Data.DatabaseInitializers { public class MyCustomDbInit<TContext> : IDatabaseInitializer<TContext> where TContext : DbContext { public void InitializeDatabase(TContext context) { // Create our database if it doesn''t already exist. context.Database.CreateIfNotExists() // Do you want to migrate to latest in your initializer? Add code here! // Do you want to seed data in your initializer? Add code here! } } }

Los resultados

Si utiliza un enfoque de código primero, deshabilita las migraciones automáticas y usa un DatabaseInitializer personalizado como el anterior, tendrá un muy buen control sobre lo que está sucediendo y cuándo.

Usamos estas estrategias en el trabajo y tenemos cero problemas (aunque nos costó un poco resolver estas estrategias). ¡Espero que encuentres un éxito similar!


Pude replicar su problema usando SQL CE, así como usar el código EF primero usando el código anterior.

Lo extraño es que cuando utilicé su código tal como está, la primera vez, funcionó perfectamente. Para que surja su problema, tuve que eliminar realmente la tabla _MigrationHistory en mi archivo .sdf.

Al eliminar el archivo .sdf (me doy cuenta de que esta podría no ser una opción en su caso, pero lo abordaré más adelante) La próxima vez que se ejecutó, creó la tabla de migración, pero aún así no funcionó correctamente. Si te das cuenta, en el paso 12 finalmente crea la tabla.

- instrucción # 1 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [Dummies] AS [Extent1]) AS [GroupBy1]

- instrucción # 2 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- instrucción # 3 WARN: System.Data.SqlServerCe.SqlCeException (0x80004005): la tabla especificada no existe. [__MigraciónHistoria]

- instrucción # 4 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- declaración # 5 WARN: System.Data.SqlServerCe.SqlCeException (0x80004005): la tabla especificada no existe. [__MigraciónHistoria]

- instrucción # 6 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- instrucción # 7 WARN: System.Data.SqlServerCe.SqlCeException (0x80004005): la tabla especificada no existe. [__MigraciónHistoria]

- instrucción # 8 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- declaración # 9 WARN: System.Data.SqlServerCe.SqlCeException (0x80004005): la tabla especificada no existe. [__MigraciónHistoria]

- declaración # 10 comenzar la transacción con nivel de aislamiento: Serializable

- declaración # 11 CREAR TABLA [Dummies] ([DummyId] [int] NOT NULL IDENTITY, [test] nvarchar, [addThis] nvarchar, CONSTRAINT [PK_Dummies] CLAVE PRIMARIA ([DummyId]))

- declaración # 12 CREATE TABLE [__MigrationHistory] ([MigrationId] nvarchar NOT NULL, [CreatedOn] [datetime] NOT NULL, [Model] [image] NOT NULL, [ProductVersion] nvarchar NOT NULL, CONSTRAINT [PK___MigrationHistory] PRIMARY KEY ( [MigrationId]))

- declaración # 13 INTRODUZCA EN [__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES (''201208101940587_InitialCreate'', ''2012-08-10T19: 40: 59.055'',, ''4.3.1 '')

- declaración # 14 compromiso de transacción

- instrucción # 15 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [Dummies] AS [Extent1]) AS [GroupBy1]

Lo que soluciona el problema creando la tabla en el lugar correcto:

Una vez que hice esto, la próxima vez que se ejecutó el código, todo volvió a funcionar perfectamente.

- instrucción # 1 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- instrucción # 2 SELECCIONE ARRIBA (1) [c]. [ProductVersion] AS [ProductVersion] FROM [__MigrationHistory] AS [c]

- instrucción # 3 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- instrucción # 4 SELECCIONE [Extensión1]. [Id. de migración] COMO [Id. de migración] DE [__ Historia de migración] COMO [Extensión1]

- instrucción # 5 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- instrucción # 6 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- instrucción # 7 SELECT TOP (1) [Project1]. [C1] AS [C1], [Project1]. [MigrationId] AS [MigrationId], [Project1]. [Model] AS [Model] FROM (SELECT [Extent1 ]. [MigrationId] AS [MigrationId], [Extent1]. [CreatedOn] AS [CreatedOn], [Extent1]. [Model] AS [Model], 1 AS [C1] FROM [__MigrationHistory] AS [Extent1]) AS [ Proyecto1] ORDEN POR [Proyecto1]. [CreatedOn] DESC

- instrucción # 8 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [__MigrationHistory] AS [Extent1]) AS [GroupBy1]

- declaración # 9 SELECCIONE ARRIBA (1) [Proyecto1]. [C1] COMO [C1], [Proyecto1]. [Id. de migración] COMO [Id. de migración], [Proyecto1]. [Modelo] COMO [Modelo] DESDE (SELECCIONE [Extensión1 ]. [MigrationId] AS [MigrationId], [Extent1]. [CreatedOn] AS [CreatedOn], [Extent1]. [Model] AS [Model], 1 AS [C1] FROM [__MigrationHistory] AS [Extent1]) AS [ Proyecto1] ORDEN POR [Proyecto1]. [CreatedOn] DESC

- declaración # 10 comenzar la transacción con nivel de aislamiento: Sin especificar

- declaración # 11 insertar valores [Dummies] ([test], [addThis]) (null, null);

seleccione [DummyId] de [Dummies] donde [DummyId] = @@ IDENTITY

- declaración # 12 insertar valores [Dummies] ([test], [addThis]) (null, null);

seleccione [DummyId] de [Dummies] donde [DummyId] = @@ IDENTITY

- declaración # 13 insertar valores [Dummies] ([test], [addThis]) (null, null);

seleccione [DummyId] de [Dummies] donde [DummyId] = @@ IDENTITY

- declaración # 14 insertar valores [Dummies] ([test], [addThis]) (null, null);

seleccione [DummyId] de [Dummies] donde [DummyId] = @@ IDENTITY

- declaración # 15 compromiso transacción

- instrucción # 16 SELECCIONE [GroupBy1]. [A1] AS [C1] FROM (SELECT COUNT (1) AS [A1] FROM [Dummies] AS [Extent1]) AS [GroupBy1]

Si descartar todo el SDF y dejar que se vuelva a crear no es una opción, esto es lo que hice para recuperarlo.

Cree una cadena de conexión en su appconfig (me doy cuenta de que probablemente desee cadenas de conexión dinámicas, por eso está en su Código, pero esto debería ser una cosa de una sola vez). Mi cadena de conexión se veía así:

<connectionStrings> <add connectionString="Data Source=MyContext.sdf;Persist Security Info=False;" name="MyContext" providerName="System.Data.SqlServerCe.4.0"/> </connectionStrings>

Cambie el constructor de su Contexto para que use la cadena de conexión:

public MyContext() //: base(string.Format(@"DataSource=""{0}""", "MyContext.sdf")) : base("MyContext")

Todo esto es necesario para que pueda ejecutar algunos comandos en la Consola del administrador de paquetes, para que vuelva a crear la tabla. Abra la consola del administrador de paquetes y ejecute este comando:

add-migration initial -ignorechanges

A continuación, ejecute el programa: emitirá algunas advertencias, pero luego creará la tabla por usted y la rellenará. Después de eso, puedes volver a cambiar tu constructor y no tuve más problemas.

Nota: una vez que comenzó a funcionar, la función de semilla comenzó a funcionar también