Este post trata sobre cómo utilizar el enfoque "Database First" en Entity Framework Core. Para aquellos de ustedes que no estén familiarizados con Entity Framework, se trata de un marco de trabajo de Microsoft que facilita la creación de aplicaciones de bases de datos como vimos en uno de los posts anteriores.
Índice
1 - Qué es database first?
El enfoque Database First significa que primero se crea la base de datos y luego se genera el modelo de Entity Framework Core a partir de ella. Esto puede ser útil cuando ya se tiene una base de datos existente o cuando se desea tener un mayor control sobre la estructura de la base de datos.
2 - Implementar Database First con Entity Framework Core
Antes de ir a la propia implementación quiero decir que vamos a utilizar la base de datos MySQL a través de docker, pero que este enfoque funciona con cualquier base de datos relacional, el único cambio es, además de la propia sintaxis en los scripts, la connection string en el código C#.
El resto debería ser agnóstico a la base de datos.
Para ello, lo primero que vamos a hacer es crear la base de datos, en este caso es simple, un par de tablas con una relación entre ellas
CREATE DATABASE IF NOT EXISTS `cursoEF`;
USE `cursoEF`;
CREATE TABLE IF NOT EXISTS `userid` (
`UserId` int(11) NOT NULL AUTO_INCREMENT,
`UserName` varchar(50) NOT NULL,
PRIMARY KEY (`UserId`),
UNIQUE KEY `UserName` (`UserName`)
);
CREATE TABLE IF NOT EXISTS `wokringexperience` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`UserId` int(11) NOT NULL,
`Name` varchar(150) NOT NULL,
`Details` varchar(5000) NOT NULL,
`Environment` varchar(500) NOT NULL,
`StartDate` date DEFAULT NULL,
`EndDate` date DEFAULT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`UserId`) REFERENCES `userid`(`UserId`)
);
Y si te conectas con una interfaz de usuario, esto es lo que deberías de poder ver:
2.1 - Importar las entidades de una base de datos con Entity Framework Core
Para comenzar, primero debemos tener una aplicación creada, en mi caso es una Web API (disponible en GitHub) y le debemos instalar el paquete Microsoft.EntityFrameworkCore.Design
.
El cual lo podemos añadir de forma manual, o con el siguiente comando:
dotnet add package Microsoft.EntityFrameworkCore.Design
En mi caso particular utilizo mysql, por lo que tengo que añadir, además, el paquete de MySQL:
dotnet add package MySql.EntityFrameworkCore
Si estás utilizando otra base de datos, como mariaDB o SQL Server, los paquetes a instalar son diferentes, te recomiendo que mires la documentación.
Una vez el proceso ha terminado, lo que vamos a querer hacer es importar las tablas de la base de datos como entidades, y para ello corremos el comando scaffold de entity framework core:
dotnet ef dbcontext scaffold "server=127.0.0.1;port=3306;database=cursoEF;user=cursoEFuser;password=cursoEFpass" MySql.EntityFrameworkCore -o Models
En mi caso estoy ejecutándolo con el usuario que he indicado, pero puedes poner la string de conexión que corresponda a tu base de datos.
Finalmente la opción -o Models
indica donde irán ubicadas las clases generadas por la herramienta.
Cuando termina, podemos ver algo así:
Como podemos ver nos crea tanto el DBContext que representa a la base de datos como un DBSet para cada una de las tablas.
Por ejemplo esta es la entidad que nos ha generado para representar la tabla userId
public partial class Userid
{
public int UserId1 { get; set; }
public string UserName { get; set; } = null!;
public virtual ICollection<Wokringexperience> Wokringexperiences { get; } = new List<Wokringexperience>();
}
3 - Mejorar la implementación por defecto de Entity Framework
Por defecto, la conexión a la base de datos nos viene dentro de nuestro DBContext
y esto lo tenemos que cambiar, porque no es seguro, de hecho, viene con un warning por defecto.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
=> optionsBuilder.UseMySQL("server=127.0.0.1;port=4306;database=cursoEF;user=cursoEFuser;password=cursoEFpass");
Lo que debemos hacer configurar nuestro DBContext
dentro de nuestro contenedor de dependencias, que dependiendo en qué versión de .NET estés, estará en la clase startup.cs
o en program.cs
además idealmente la conexión deberá estar en el fichero de configuración para poder utilizar Iconfiguration o incluso mejor, en un vault de gestión de credenciales. El código nos quedará algo así:
builder.Services.AddDbContext<CursoEfContext>(options =>
options.UseMySQL(builder.Configuration.GetConnectionString("cursoEF")
?? throw new Exception("missing connectionstring")));
Y esta acción nos permite eliminar el método Onconfiguring
de nuestro DBContext
.
4 - Ejemplo de aplicación C# con Entity Framework
Ahora solo nos queda probar la app, para este ejemplo voy a hacer un código sencillo, voy a ejecutar todo desde el controlador, si ves mi canal sabes que nunca recomiendo hacer esto, pero para una prueba de concepto, pues es aceptable.
- Mi post sobre la estructura de una aplicación C# - -
Pero para este caso, simplemente vamos a tener un endpoint para insertar los datos:
[ApiController]
[Route("[controller]")]
public class CursoEFController : ControllerBase
{
private readonly CursoEfContext _context;
public CursoEFController(CursoEfContext context)
{
_context = context;
}
[HttpPost]
public async Task<int> AddUser(UserDto userDto)
{
Userid user = new Userid()
{
UserName = userDto.UserName
};
await _context.Userids.AddAsync(user);
await _context.SaveChangesAsync();
return user.UserId1;
}
}
public record UserDto(string UserName);
Y podemos ver el resultado:
Recuerda que para hacer acciones CRUD puedes utilizar LINQ
sobre el DBSet
.
5 - Actualizar el modelo utilizando Database First en EF Core
Para actualizar el modelo, osea las clases de C# dentro de nuestro proyecto cuando la estructura de la base de datos es actualizada debemos ejecutar el mismo comando que para hacer la ejecución inicial, únicamente incluyendo la opción --force
dotnet ef dbcontext scaffold "server=127.0.0.1;port=3306;database=cursoEF;user=cursoEFuser;password=cursoEFpass" MySql.EntityFrameworkCore -o Models --force
Y con esto finalizamos la parte de Database First, donde tenemos la base de datos ya creada cuando empezamos a desarrollar.