EntityFramework con PostgresSQL
Tags: dotnet, orm
Published: 2023-11-12 17:30:23

Principalmente entender que EntityFramework tiene un parquete para cada una de las funciones que se necesite, en este caso usaremos 3

## Paquetes

**Microsoft.EntityFrameworkCore**
El paquete Microsoft.EntityFrameworkCore es una biblioteca de acceso a datos para .NET Core que proporciona una API para acceder a bases de datos relacionales, NoSQL y de documentos.
[Nuget page](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore)

**Microsoft.EntityFrameworkCore.Tools**

El paquete Microsoft.EntityFrameworkCore.Tools proporciona una serie de herramientas para el desarrollo de aplicaciones con Entity Framework Core. Estas herramientas incluyen:

- Generador de modelos: Genera clases de entidad a partir de una base de datos existente.
- Herramienta de migración: Crea y aplica cambios a la base de datos.
- Herramienta de depuración: Ayuda a depurar problemas de acceso a datos.

[Nuget page](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Tools/)

**Npgsql.EntityFrameworkCore.PostgreSQL**
El paquete Npgsql.EntityFrameworkCore.PostgreSQL es un proveedor de Entity Framework Core para PostgreSQL. Esto significa que permite que las aplicaciones .NET Core accedan a bases de datos PostgreSQL utilizando Entity Framework Core.

[Nuget page](https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL/)

## Implementacion en el codigo
Para comenzar a hacer la implementacion debemos tener en cuenta lo que agregaremos, en este caso será un contexto y los modelos de las bases de datos

- **Context**: El contexto es la principal interfaz entre tu aplicación y tu base de datos. Te permite consultar y guardar datos en tu base de datos.

- **Models**: Los modelos en tu código representan las entidades de tu aplicación y cómo estas entidades se relacionan entre sí. En términos de bases de datos, un modelo generalmente representa una tabla.

Primeramente crearemos los modelos de nuestra aplicacion, en este caso los crearé en una carpeta llamada `Models` y dentro de esta carpeta irá el archivo llamado `Todo.cs` ya que esta será una tabla en mi base de datos, debemos crear un archivo para cada tabla (para mayor comodidad)

```csharp
// Models/Todo.cs

// Importando el espacio de nombres System.ComponentModel.DataAnnotations para usar las anotaciones de datos
using System.ComponentModel.DataAnnotations;

// Define el espacio de nombres para el modelo Todos
namespace todoBackend.Models
{
    // Define la clase Todos
    public class Todos
    {
        // El atributo [Key] especifica que TodoId es la clave primaria en la base de datos
        // Guid.NewGuid() genera un nuevo identificador único cada vez que se crea un nuevo elemento Todo
        [Key]
        public Guid TodoId { get; set; } = Guid.NewGuid();

        // Propiedad string nullable para el contenido del elemento Todo
        public string? TodoContent { get; set; }

        // Propiedad boolean nullable para el estado de finalización del elemento Todo, por defecto es false
        public bool? TodoCompleted { get; set; } = false;

        // Propiedad DateTime nullable para la fecha de creación del elemento Todo, por defecto es la fecha y hora actuales
        public DateTime? TodoDateCreated { get; set; } = DateTime.Now;

        // Propiedad DateTime nullable para la última fecha de actualización del elemento Todo, por defecto es null
        public DateTime? TodoDateUpdated { get; set; } = null;

        // Propiedad DateTime nullable para la fecha de finalización del elemento Todo, por defecto es null
        public DateTime? TodoDateCompleted { get; set; } = null;
    }
}
```

Recuerda que este es un simple ejemplo, tu tendrás que implementar la logica como lo veas necesario en tu proyecto.

Teniendo este entendido empezaremos a agregar el contexto, en este caso crearé una carpeta llamada `Data` y dentro de esta carpeta irá un archivo llamado `AppDbContext.cs`.
En este archivo comenzaremos a colocar nuestro codigo

```csharp
// Data/AppDbContext.cs

// EntityFrameworkCore para trabajar con la clase DbContext
using Microsoft.EntityFrameworkCore;
// Los modelos que tenemos en nuestro codigo
using todoBackend.Models;

// Define el espacio de nombres para el contexto de la base de datos
namespace todoBackend.Data
{
    // Define la clase AppDbContext que hereda de DbContext
    public class AppDbContext : DbContext
    {
        // Define una variable para almacenar la configuración de la aplicación
        protected readonly IConfiguration Configuration;

        // Define el constructor de la clase que toma una configuración como parámetro
        // para posteriormente asignarla a la variable Configuration
        public AppDbContext(IConfiguration configuration)
        {
            // Asigna la configuración a la variable Configuration
            Configuration = configuration;
        }

        // Este método se llama cuando se está configurando el contexto de la base de datos
        // y se utiliza para configurar las opciones de la base de datos
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            // Conecta a la base de datos PostgreSQL utilizando la cadena de conexión de la configuración de la aplicación
            // que se encuentra en el archivo appsettings.json
            options.UseNpgsql(Configuration.GetConnectionString("WebApiDatabase"));
        }

        // Define una propiedad para cada tabla en la base de datos
        public DbSet<Todos> Todos { get; set; }
    }
}
```

En nuestro `appsettings.json` agregaremos un nuevo atributo el cual seria WebApiDatabase, esta será la configuracion de nuestra conexion

```json
// appsettings.json

 "ConnectionStrings": {
    "WebApiDatabase": "Host=host; Database=db; Username=postgres; Password=pass"
  }
```

En nuestro Program.cs agregaremos el contexto de nuestra base de datos
```csharp
// Program.cs

// Agrega el DbContext al contenedor de inyección de dependencias
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseNpgsql(builder.Configuration.GetConnectionString("WebApiDatabase")));
```

## Migracion

Las migraciones en Entity Framework son una forma de actualizar el esquema de una base de datos sin perder datos. Permiten a los desarrolladores realizar cambios en el modelo de datos de su aplicación sin tener que crear y aplicar manualmente las modificaciones a la base de datos.

Esta herramienta está presente en el CLI de Entity Framework, para tenerlo lo instalaremos con el siguiente comando
```
dotnet tool install --global dotnet-ef
```

El siguiente comando crea una migración inicial. Esta migración se utiliza para crear la estructura del esquema de base de datos inicial. El nombre de la migración se puede personalizar, pero por defecto es "Init", esto creará una carpeta llamada `Migrations` en nuestro proyecto, nosotros dejemosla quieta ya que internamente funcionará mas adelante por si sola 
```
dotnet ef migrations add "init"
```

Finalmente, el siguiente comando aplica las migraciones a la base de datos. En este caso, la migración inicial que se creó en el paso anterior.
```
dotnet ef database update
```

Luego de esto podremos ver reflejada la migracion en nuestra base de datos ✅.

## Uso en el codigo

Al momento de usarlo en el codigo no entraré mucho en detalles ya que simplemente depende en cada una de las necesiadades del proyecto y del programador.

En este caso agregaré las siguientes lineas, estas líneas de código establecen una conexión entre la clase `TodoService` y el contexto de la base de datos `AppDbContext`. Esta conexión es esencial para que la clase `TodoService` pueda interactuar con la base de datos y realizar operaciones como la recuperación, actualización o eliminación de datos relacionados con las tareas ("todos").

```csharp
// Services/TodoService.cs

// Importamos nuestro contexto previamente creado
using todoBackend.Data;

// Se declara una variable miembro llamada '_context' de tipo 'AppDbContext'.
private readonly AppDbContext _context;

// Constructor de la clase 'TodoService' que toma un parámetro 'context' de tipo 'AppDbContext'.
public TodoService(AppDbContext context)
{
    // Se asigna el valor del parámetro 'context' a la variable miembro '_context'.
    _context = context;
}
```

Con esto ahora podremos comenzar a usar la variable `_context` para manejar la base de datos, es importante mencionar que para manejar las tablas debemos usar `_context. <table>` o en mi caso `_context.Todos` ya que previamente definimos la porpiedad con la siguiente linea de codigo
```csharp
public DbSet<Todos> Todos { get; set; }
```
Si tuviera otra tabla en mis modelos tendria que definir esa propiedad.