Propagación de datos

La siembra de datos es el proceso de llenar una base de datos con un conjunto inicial de datos.

Hay varias maneras de hacerlo en EF Core:

  • Datos de inicialización del modelo
  • Personalización manual de la migración
  • Lógica de inicialización personalizada

Datos de inicialización del modelo

A diferencia de EF6, en EF Core, los datos de propagación se pueden asociar a un tipo de entidad como parte de la configuración del modelo. Las migraciones de EF Core pueden luego calcular automáticamente las operaciones de inserción, actualización y eliminación que hay que aplicar al actualizar la base de datos a una nueva versión del modelo.

Nota:

Las migraciones solo consideran los cambios del modelo al determinar qué operación se debe realizar para obtener los datos de inicialización en el estado deseado. Por lo tanto, los cambios realizados fuera de las migraciones pueden perderse o provocar un error.

Por ejemplo, esto configurará los datos de inicialización para Blog enOnModelCreating:

modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Url = "http://sample.com" });

Para agregar entidades que tengan una relación, se deben especificar los valores de clave externa:

modelBuilder.Entity<Post>().HasData(
    new Post { BlogId = 1, PostId = 1, Title = "First post", Content = "Test 1" });

Si el tipo de entidad tiene propiedades en estado de sombra, se puede usar una clase anónima para proporcionar los valores:

modelBuilder.Entity<Post>().HasData(
    new { BlogId = 1, PostId = 2, Title = "Second post", Content = "Test 2" });

Los tipos de entidad propiedad se pueden inicializar de forma similar:

modelBuilder.Entity<Post>().OwnsOne(p => p.AuthorName).HasData(
    new { PostId = 1, First = "Andriy", Last = "Svyryd" },
    new { PostId = 2, First = "Diego", Last = "Vega" });

Vea el proyecto de ejemplo completo para obtener más contexto.

Una vez agregados los datos al modelo, se deben usar migraciones para aplicar los cambios.

Sugerencia

Si necesita aplicar migraciones como parte de una implementación automatizada, puede crear un script SQL que se pueda obtener una vista previa antes de la ejecución.

Como alternativa, puede usar context.Database.EnsureCreated() para crear una nueva base de datos que contenga los datos de inicialización, por ejemplo, para una base de datos de prueba o cuando se usa el proveedor en memoria o cualquier base de datos no relacional. Tenga en cuenta que si la base de datos ya existe, EnsureCreated() no actualizará el esquema ni los datos de inicialización de la base de datos. En el caso de las bases de datos relacionales, no debe llamar EnsureCreated() si planea usar migraciones.

Limitaciones de los datos de inicialización del modelo

Este tipo de datos de inicialización se administra mediante migraciones y el script para actualizar los datos que ya están en la base de datos deben generarse sin conectarse a la base de datos. Esto impone algunas restricciones:

  • El valor de clave principal debe especificarse incluso si normalmente lo genera la base de datos. Se usará para detectar cambios de datos entre migraciones.
  • Los datos iniciales anteriores se quitarán si se cambia la clave principal de cualquier manera.

Por lo tanto, esta característica es más útil para los datos estáticos que no se espera que cambien fuera de las migraciones y no dependan de nada más en la base de datos, por ejemplo, códigos postales.

Si el escenario incluye cualquiera de las siguientes opciones, se recomienda usar la lógica de inicialización personalizada descrita en la última sección:

  • Datos temporales para pruebas
  • Datos que dependen del estado de la base de datos
  • Los datos que son grandes (los datos de inicialización se capturan en instantáneas de migración y los datos de gran tamaño pueden provocar rápidamente archivos enormes y un rendimiento degradado).
  • Datos que necesitan que la base de datos genere valores clave, incluidas las entidades que usan claves alternativas como identidad
  • Datos que requieren transformación personalizada (que no se manipula mediante conversiones de valor), como algunos hash de contraseñas
  • Datos que requieren llamadas a una API externa, como ASP.NET roles de Core Identity y la creación de usuarios

Personalización manual de la migración

Cuando se agrega una migración, los cambios en los datos especificados con HasData se transforman en llamadas a InsertData(), UpdateData(), y DeleteData(). Una manera de solucionar algunas de las limitaciones de HasData es agregar manualmente estas llamadas o operaciones personalizadas a la migración en su lugar.

migrationBuilder.InsertData(
    table: "Blogs",
    columns: new[] { "Url" },
    values: new object[] { "http://generated.com" });

Lógica de inicialización personalizada

Una manera sencilla y eficaz de realizar la propagación de datos es usar DbContext.SaveChanges() antes de que la lógica de aplicación principal comience la ejecución.

using (var context = new DataSeedingContext())
{
    context.Database.EnsureCreated();

    var testBlog = context.Blogs.FirstOrDefault(b => b.Url == "http://test.com");
    if (testBlog == null)
    {
        context.Blogs.Add(new Blog { Url = "http://test.com" });
    }

    context.SaveChanges();
}

Advertencia

El código de propagación no debe formar parte de la ejecución normal de la aplicación, ya que esto puede provocar problemas de simultaneidad cuando se ejecutan varias instancias y también requeriría que la aplicación tenga permiso para modificar el esquema de la base de datos.

En función de las restricciones de la implementación, el código de inicialización se puede ejecutar de maneras diferentes:

  • Ejecución de la aplicación de inicialización localmente
  • Implementar la aplicación de inicialización con la aplicación principal, invocando la rutina de inicialización y deshabilitando o quitando la aplicación de inicialización.

Normalmente, esto se puede automatizar mediante perfiles de publicación.