Migración a .Net Core 2.0: Diferencias en la mapeo de relaciones muchos a muchos en EF Core

En EF Core, las relaciones muchos a muchos no están directamente soportadas y deben implementarse mediante dos relacioens uno a muchos. El mapeo de estas relaciones se basa en convenciones, donde el nombre del campo de clave foránea por defecto es (nombreDeLaEntidadReferenciada + nombreDeLaClavePrimaria) o simplemente el nombre de la clave primaria de la entidad referenciada.

public class Producto
{
    [Key]
    public int IdProducto { get; set; }
    public string CodigoProducto { get; set; }
    public string NombreProducto { get; set; }

    public List<CategoriaProducto> CategoriaProductos { get; set; }
}

public class Categoria
{
    [Key]
    public int IdCategoria { get; set; }
    public string CodigoCategoria { get; set; }
    public string NombreCategoria { get; set; }

    public List<CategoriaProducto> CategoriaProductos { get; set; }
}

Por ejemplo, si la antidad Producto tiene una relación con Categoria, la consulta SQL buscará un campo IdCategoria en la tabla Producto.

Para definir una relación muchos a muchos entre Producto y Categoria, se necesita una clase intermedia:

/// <summary>
/// EF CORE maneja las relaciones muchos a muchos como dos relaciones uno a muchos.
/// </summary>
public class CategoriaProducto
{
    public int Id { get; set; }
    public int IdCategoria { get; set; }
    public Categoria Categoria { get; set; }
    public int IdProducto { get; set; }
    public Producto Producto { get; set; }
}

Se debe configurar el mapeo en el método OnModelCreating del contexto:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<CategoriaProducto>().HasKey(cp => new { cp.IdCategoria, cp.IdProducto });

    modelBuilder.Entity<CategoriaProducto>()
        .HasOne(cp => cp.Categoria)
        .WithMany(c => c.CategoriaProductos)
        .HasForeignKey(cp => cp.IdCategoria);

    modelBuilder.Entity<CategoriaProducto>()
        .HasOne(cp => cp.Producto)
        .WithMany(p => p.CategoriaProductos)
        .HasForeignKey(cp => cp.IdProducto);
}

Con esta configuración, para obtener todos los productos de una categoría específica, se puede usar:

var productos = _context.CategoriaProductos
    .Where(cp => cp.IdCategoria == 1)
    .Include(cp => cp.Producto)
    .Select(cp => cp.Producto)
    .ToList();

O bien, para evitar consultas múltiples, se puede hacer lo siguiente:

var idsProductos = _context.CategoriaProductos
    .Where(cp => cp.IdCategoria == 1)
    .Select(cp => cp.IdProducto)
    .ToList();

var productos = _context.Productos
    .Where(p => idsProductos.Contains(p.IdProducto))
    .ToList();

var resultado = new { total = productos.Count, filas = productos };
return Json(resultado);

Si la relación entre Categoria y Producto fuera uno a muchos, la estructura de las entidades sería:

public class Producto
{
    [Key]
    public int IdProducto { get; set; }
    public string CodigoProducto { get; set; }
    public string NombreProducto { get; set; }

    public int IdCategoria { get; set; }
    public Categoria Categoria { get; set; }
}

public class Categoria
{
    [Key]
    public int IdCategoria { get; set; }
    public string CodigoCategoria { get; set; }
    public string NombreCategoria { get; set; }

    public List<Producto> Productos { get; set; }
}

Y el mapeo en OnModelCreating:

modelBuilder.Entity<Producto>()
    .HasOne(p => p.Categoria)
    .WithMany(c => c.Productos)
    .HasForeignKey(p => p.IdCategoria);

Para obtener todos los productos de una categoría, se puede usar:

var categoria = _context.Categorias
    .Where(c => c.IdCategoria == id)
    .Include(c => c.Productos)
    .FirstOrDefault();

var productos = categoria.Productos
    .Select(p => new
    {
        p.IdProducto,
        p.CodigoProducto,
        p.NombreProducto,
        p.NombreCompletoProducto,
        p.IdCategoria,
        CategoriaNombre = p.Categoria.NombreCategoria
    })
    .ToList();

var resultado = new { total = productos.Count, filas = productos };
return Json(resultado);

Esto evita el problema de bucles de referencia al sreializar a JSON.

Etiquetas: .NET Core Entity Framework Core migración Relaciones Many-to-Many Relaciones One-to-Many

Publicado el 8-27 00:49