Migraciones de Base de Datos con EF Core y Code First en .NET Core

En el artículo anterior, explicamos cómo utilizar EF7 para generar una base de datos mediante el enfoque Code First.

Sin embargo, existen varios aspectos que requieren atención. En este artículo, abordaremos estos problemas de manera gradual. Son soluciones sencillas, aunque dispersas.

  1. Lectura de archivos de configuración para obtener cadenas de conexión
  2. Implementación de operaciones CRUD con la base de datos
  3. Actualización de la base de datos cuando se modifican las clases de entidad

I. Lectura de archivos de configuración para obtener cadenas de conexión

En el artículo anterior, nuestra cadena de conexión estaba escrita directamente en el código.

Incluir la cadena de conexión directamente en el programa es una práctica muy poco recomendable. Por esta razón, la hemos movido al archivo de configuración appsettings.json.

1 {
2   "Logging": {
3     "IncludeScopes": true,
4     "LogLevel": {
5       "Default": "Warning"
6     }
7   },
8   "ConStr": "server=.;Database=DBCodeFirst;uid=sa;pwd=12346"
9 }

El siguiente paso es leer esta configuración. Dado que es un archivo de configuración, toda la aplicación lo utilizará, por lo que es necesario encapsular su acceso.

Hemos investigado diferentes enfoques encontrados en línea. Algunos desarrolladores pueden leer directamente la configuración en un objeto, pero evitaremos esas operaciones complejas por el momento.

Optaremos por el método más sencillo: pares clave-valor, similar a cómo se leían los archivos webconfig en el pasado.

Primero, es necesario agregar tres paquetes. Edite directamente el archivo commom.csproj:

1     <PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
2     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
3     <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />

Luego, cree una nueva clase llamada Configuracion:

 1 using Microsoft.Extensions.Configuration;
 2 using Microsoft.Extensions.DependencyInjection;
 3 using Microsoft.Extensions.Options;
 4 using System;
 5 using System.Diagnostics;
 6 
 7 namespace Comun
 8 {
 9     /// <summary>
10     /// Clase para manejar la configuración de la aplicación
11     /// </summary>
12     public class Configuracion
13     {
14         /// <summary>
15         /// Todos los datos de configuración
16         /// </summary>
17         private static IConfigurationRoot ConfiguracionDatos { get; set; }
18 
19         /// <summary>
20         /// Establece la configuración
21         /// </summary>
22         /// <param name="_configuracion">Objeto de configuración</param>
23         public static void EstablecerConfiguracion(IConfigurationRoot _configuracion)
24         {
25             ConfiguracionDatos = _configuracion;
26         }
27 
28         /// <summary>
29         /// Retorna el valor correspondiente a una clave
30         /// </summary>
31         /// <typeparam name="T">Tipo de dato del valor</typeparam>
32         /// <param name="clave">Clave de configuración</param>
33         /// <param name="defecto">Valor por defecto</param>
34         /// <returns>Valor de configuración</returns>
35         public static T ObtenerValor<T>(string clave, T defecto = default(T))
36         {
37             try
38             {
39                 defecto = (T)Convert.ChangeType(ConfiguracionDatos.GetSection(clave).Value, typeof(T));
40             }
41             catch (Exception e)
42             {
43                 Debug.WriteLine(e.Message);
44             }
45             return defecto;
46         }
47 
48     }
49 
50 }

Es necesario realizar una configuración adicional en el método Startup del archivo Startup.cs:

1         public Startup(IConfiguration configuracion)
2         {
3             IConfigurationRoot config = new ConfigurationBuilder()
4                   .SetBasePath(Directory.GetCurrentDirectory())
5                   .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
6                   .Build();
7 
8             Configuracion.EstablecerConfiguracion(config);
9         }

El uso es muy sencillo con una sola línea de código:

1         // Cadena de conexión a la base de datos
2         string cadenaConexion = Configuracion.ObtenerValor<string>("ConStr");

Dado que el archivo de configuración está en formato JSON, es probable que tenga una estructura jerárquica. Por ejemplo, para leer el valor de "IncludeScopes" dentro de "Logging", podemos hacerlo así:

1          string incluirAlcance = Configuracion.ObtenerValor<string>("Logging:IncludeScopes");

II. Implementación de operaciones CRUD con la base de datos

Esta sección trata sobre un problema encontrado al intentar realizar operaciones CRUD.

Con EF en versiones anteriores, ya sea usando DBFirst, CodeFirst o ModelFirst, el acceso a la base de datos era directo: simplemente se creaba una nueva instancia del contexto.

1    // Contexto de base de datos
2    var BD = new DBCodeFirst();

Sin embargo, después de generar la base de datos con CodeFirst e intentar agregar datos de prueba, encontré un error:

No hay proveedor de base de datos configurado para este DbContext.<br></br>Un proveedor puede configurarse anulando el método DbContext.OnConfiguring o usando AddDbContext con un proveedor de servicios de aplicación.<br></br>Si se utiliza AddDbContext, asegúrese de que el constructor de su tipo DbContext acepte un objeto DbContextOptions<TContext> y lo pase al constructor base de DbContext."

La solución encontrada fue la siguiente:

Aunque no he trabajado aún con inyección de dependencias, este código resultó un poco confuso.

Parece estar relacionado con el método ConfigureServices en la clase Startup. En próximos artículos profundizaremos en este tema.

Afortunadamente, ya podemos acceder a la base de datos. El siguiente paso es implementar una arquitectura de tres capas, lo que aún está pendiente.

III. Actualización de la base de datos cuando se modifican las clases de entidad

Primero, recordemos los comandos utilizados en el artículo anterior para generar la base de datos:

 Add-Migration Inicial
 y
 Update-DataBase Inicial

Si agregamos nuevas clases de entidad o modificamos campos en las clases existentes, necesitaremos actualizar la base de datos.

En este caso, ya tenemos la base de datos generada y la carpeta Migrations, por lo que podemos continuar usando los mismos comandos con una ligera modificación:

Add-Migration Actualizacion
y
Update-DataBase Actualizacion

Es importante recordar que el último parámetro (el nombre de la migración) debe ser diferente cada vez.

Por ejemplo, si la primera migración se llamó Inicial, la primera actualización podría ser Actualizacion. Las migraciones posteriores tendrían nombres como Actualizacion01, Actualizacion02, Actualizacion03, etc.

Cada migración agregará contenido correspondiente a la carpeta Migrations, y la tabla __EFMigrationsHistory registrará estas migraciones.

Etiquetas: EF Core Code First .NET Core migraciones de base de datos Entity Framework

Publicado el 7-12 18:09