Introducción

Hasta el momento, hemos trabajado con objetos de manera individual. Siguiendo nuestro ejemplo de ParkingApp llego el momento de comenzar a trabajar con objetos relacionados. Nos enfocaremos en la relación entre los vehículos y estacionamientos.

Modelo

Sabemos que un vehículo puede estacionar muchas veces dentro de un estacionamiento, es por eso que definimos una propiedad dentro de vehículo de tipo List<T> para los estacionamientos. Por otro lado, un estacionamiento tiene una propiedad de vehículo con su clave externa llamada VehicleId. 

[Table("Vehicle")]
public class Vehicle
{
    public int Id { get; set; }
    public int CateogoryId { get; set; }

    [Required, StringLength(10)]
    public string LicensePlate { get; set; }

    public Category Category { get; set; }
    public List<Parking> Parkings { get; set; }
}
[Table("Parking")]
public class Parking
{
    public int Id { get; set; }
    public int VehicleId { get; set; }

    [Required, StringLength(10)]
    public string Ticket { get; set; }

    public DateTime CheckIn { get; set; }
    public DateTime? CheckOut { get; set; }

    public Vehicle Vehicle { get; set; }
}

Insertar nuevo vehículo con estacionamiento

El siguiente código muestra como insertar un vehículo y un estacionamiento a la vez. Utilizamos la colección de estacionamientos para agregar un nuevo objeto asociado al vehículo.

private static void InsertRelatedVehicles()
{
    var vehicle = new Vehicle
    {
        CateogoryId = 1,
        LicensePlate = "JNS543",
        Parkings = new List<Parking>
        {
            new Parking
            {
                Ticket = "9485637483",
                CheckIn = DateTime.Now
            }
        }
    };

    _context.Add(vehicle);
    _context.SaveChanges();
}

Al agregar nuestro nuevo objeto vehículo al contexto, este comienza a trackear la entidad y todo lo que este vinculado a este mismo. 

Veamos que comandos SQL ejecuta ef core 2

insert_related_EfCore2
insert_related_EfCore2

Como vemos, ef core 2 esta ejecutando dos inserciones de manera individual. Lo que esta haciendo, es insertar el vehículo y devuelve el id generado. Luego, toma ese id y lo envía como parámetro en la inserción del estacionamiento. 

Insertar nuevo vehículo con múltiples estacionamientos

Siguiendo nuestro ejemplo, es posible insertar múltiples objetos hijos para un objeto individual.

private static void InsertRelatedVehicleMultipleParkings()
{
    var vehicle = new Vehicle
    {
        CateogoryId = 1,
        LicensePlate = "ABC543",
        Parkings = new List<Parking>
        {
            new Parking
            {
                Ticket = "9481637483",
                CheckIn = DateTime.Now
            },
            new Parking
            {
                Ticket = "9481639183",
                CheckIn = DateTime.Now
            }
        }
    };

    _context.Add(vehicle);
    _context.SaveChanges();
}

Veamos que comandos SQL ejecuta ef core 2 para este caso

insert_related_multiple_EfCore2
insert_related_multiple_EfCore2

Como vemos, ef core 2 vuelve a ejecutar dos inserciones distintas pero notar que el segundo comando es por lote, insertando todas los estacionamientos (en este caso 2) para un vehículo nuevo.

Insertar nuevo estacionamiento a un vehículo utilizando otra instancia del contexto (desconectado)

Es muchos escenarios suele ocurrir que necesitamos utilizar una nueva instancia de nuestro contexto dependiendo como es la arquitectura de nuestro proyecto.

En este escenario, les voy a dar la solución definitiva al problema ocurre en el uso del contexto. 

El siguiente código simula el caso

private static void InsertRelatedVehicleAnotherInstance()
{
    var vehicle = _context.Vehicles.FirstOrDefault(p => p.Id == 1);

    vehicle?.Parkings.Add(new Parking
    {
        Ticket = "6574837564",
        CheckIn = DateTime.Now
    });

    //another context
    using (var ctx = new ParkingContext())
    {
        ctx.Vehicles.Add(vehicle);
        ctx.SaveChanges();
    }
}

Como vemos, obtenemos el primer vehículo utilizando un contexto y agregamos un nuevo estacionamiento. Para guardar este vehículo, utilizamos una nueva instancia de nuestro contexto

Al ejecutar el siguiente código, ef core 2 nos arroja el siguiente error

Microsoft.EntityFrameworkCore.DbUpdateException: ‘An error occurred while updating the entries. See the inner exception for details.’

Cannot insert explicit value for identity column in table ‘Vehicle’ when IDENTITY_INSERT is set to OFF

Esto ocurre porque el nuevo contexto no tiene idea de la historia del objeto vehículo que obtuvimos con el primer contexto. La nueva instancia del contexto no trackea al vehículo que obtuvimos y al querer hacer la inserción del mismo ya existe un vehículo con ese id que auto genera ef core.

Para solucionar este problema debemos insertar el estacionamiento utilizando la clave primaria del vehículo como clave foránea en la entidad estacionamiento, tal como muestro a continuación

private static void InsertRelatedVehicleAnotherInstance(int vehicleId)
{
    var parking = new Parking
    {
        Ticket = "6574837564",
        CheckIn = DateTime.Now,
        VehicleId = vehicleId
    };

    //another context
    using (var ctx = new ParkingContext())
    {
        ctx.Parkings.Add(parking);
        ctx.SaveChanges();
    }
}

GITHUB /mpetrinidev

blog-parking-ef-core-2

Branch [insert-related-objects]

¡Si te gusto el articulo, compártelo con tus contactos en las redes sociales!

Escribe una respuesta a este comentario

avatar
500
  Subscribe  
Notificar de