简体   繁体   中英

Entity Framework “Include” does not load entity related tables

I have a project with ASP.NET Core 3.1 using Visual Studio 2019 (64 bit) and SQL Server 2019. When I run the backend it generates an error when I use Entity Framework's .Include to load data from a related table. It's strange that the same code worked with no problem using ASP.NET Core 2.1 in Visual Studio 2017. This happens to me in every method where I use .Include to load data from a related table.

The following image shows the error generated when I run the backend:

在此处输入图片说明

This is the method where I use the .Include :

namespace Sistema.Web.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ArticulosController : ControllerBase
    {
        private readonly DbContextSistema _context;

        public object Articulos { get; private set; }

        public ArticulosController(DbContextSistema context)
        {
            _context = context;
        }

        [HttpGet("[action]")]
        public async Task<IEnumerable<ArticuloViewModel>> Listar()
        {
            var articulo = await _context.Articulos.Include(a=>a.categoria).ToListAsync();
            return articulo.Select(a => new ArticuloViewModel
            {
                idarticulo = a.idarticulo,
                idcategoria = a.idcategoria,
                categoria = a.categoria.nombre,
                codigo = a.codigo,
                nombre = a.nombre,
                stock = a.stock,
                precio_venta = a.precio_venta,
                descripcion = a.descripcion,
                condicion = a.condicion
            });
        }

        private bool ArticuloExists(int id)
        {
            return _context.Articulos.Any(e => e.idarticulo == id);
        }
    }
}

This is the entity of the main table (Articulo):

namespace Sistema.Entidades.Almacen
{
    public class Articulo
    {
        public int idarticulo { get; set; }
        [Required]
        public int idcategoria { get; set; }
        public string codigo { get; set; }
        [Required]
        public string nombre { get; set; }
        [Required]
        public decimal precio_venta { get; set; }
        [Required]
        public int stock { get; set; }
        [StringLength(256)]
        public string descripcion { get; set; }
        public bool condicion { get; set; }

        public Categoria categoria { get; set; }
    }
}

This is the entity of the related table (Categoria):

namespace Sistema.Entidades.Almacen
{
    public class Categoria
    {
        public int idcategoria { get; set; }
        [Required]
        public string nombre { get; set; }
        [StringLength(256)]
        public string descripcion { get; set; }
        public bool condicion { get; set; }

        public ICollection<Articulo> articulos { get; set; }
    }
}

This is the mapping class for Articulo :

namespace Sistema.Datos.Mapping.Almacen
{
    public class ArticuloMap : IEntityTypeConfiguration<Articulo>
    {
        public void Configure(EntityTypeBuilder<Articulo> builder)
        {
            builder.ToTable("articulo")
                .HasKey(a => a.idarticulo);     
        }
    }
}

This is the mapping class for Categoria :

namespace Sistema.Datos.Mapping.Almacen
{
    public class CategoriaMap : IEntityTypeConfiguration<Categoria>
    {
        public void Configure(EntityTypeBuilder<Categoria> builder)
        {
            builder.ToTable("categoria")
                .HasKey(c => c.idcategoria);
            builder.Property(c => c.nombre)
                .HasMaxLength(50);
            builder.Property(c => c.descripcion)
                .HasMaxLength(256);
        }
    }
}

This is the DbContext where I include my ArticuloMap and CategoriaMap :

namespace Sistema.Datos
{
    public class DbContextSistema : DbContext
    {
        public DbSet<Categoria> Categorias { get; set; }
        public DbSet<Articulo> Articulos { get; set; }
        public DbSet<Rol> Roles { get; set; }
        public DbSet<Usuario> Usuarios { get; set; }
        public DbSet<Persona> Personas { get; set; }
        public DbSet<Ingreso> Ingresos { get; set; }

        public object Entry(object articulos)
        {
            throw new NotImplementedException();
        }

        public DbSet<DetalleIngreso> DetalleIngresos { get; set; }
        public DbSet<Venta> Ventas { get; set; }
        public DbSet<DetalleVenta> DetalleVentas { get; set; }

        public DbContextSistema(DbContextOptions<DbContextSistema> options) : base(options)
        {

        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            
            modelBuilder.ApplyConfiguration(new CategoriaMap());
            modelBuilder.ApplyConfiguration(new ArticuloMap());
            modelBuilder.ApplyConfiguration(new RolMap());
            modelBuilder.ApplyConfiguration(new UsuarioMap());
            modelBuilder.ApplyConfiguration(new PersonaMap());
            modelBuilder.ApplyConfiguration(new IngresoMap());
            modelBuilder.ApplyConfiguration(new DetalleIngresoMap());
            modelBuilder.ApplyConfiguration(new VentaMap());
            modelBuilder.ApplyConfiguration(new DetalleVentaMap());
        }
    }
}

Here is the db table design for articulo and categoria and the db relationships as requested:

在此处输入图片说明

Has something changed in .NET Core 3.1? Is there something I need to change or adjust to fix this issue? Thank you very much in advance for your responses!

EDIT:

  • Replaced images for source code.
  • Added source code of the entity of the related table Categoria .
  • Added source code of the mapping class CategoriaMap .
  • Added an image of the db table design and relationships.

I apologize in advance for making the post only with images instead of the source code. Thank you for your responses, I hope that the last Edit make things more clear for a solution to the problem.

There are several ways to tell EF how to wire up a relationship. By default for your objects:

//principal
public class Categoria
{
    public int idcategoria { get; set; }

    ...

    public ICollection<Articulo> articulos { get; set; }
}

//dependent
public class Articulo
{
    public int idarticulo { get; set; }
    public int idcategoria { get; set; }

    ...

    public Categoria categoria { get; set; }
}

EF will be able to see these are related, because an Articulo has a Categoria and a Categoria has many Articulo so it can infer how the 1:M relationship is structured. When guessing which column of the dependent links to the principal it tries:

  • categoria idcategoria (italics -> dependent navigation property name)
  • categoria Id
  • Categoria idcategoria (italics -> principal class name)
  • Categoria Id

Most critically it no longer (since 3.0) tries to find a pair of properties between the two classes that have identical names, which I presume was why it worked before


If your naming convention was typical English it'd have found it, but with those rules it won't find a Spanish style name so you'll have to be explicit, probably easiest by annotating either of the properties in the relationship (EF doesn't care which) - I see arguments for either way being acceptable:

  • If you do it in the dependent, then you're referencing a property in the same class so you don't have to look in another class/open another file to check what it's referring to
  • If you do it in the principal, then you're describing how a property of this class links to what property of that class

So it looks like:

//principal
public class Categoria
{
    public int idcategoria { get; set; }

    ...

    [ForeignKey("idcategoria")] //it means Articulo.idcategoria is the relevant property that links to PK of this class
    public ICollection<Articulo> articulos { get; set; }
}

OR

//dependent
public class Articulo
{
    public int idarticulo { get; set; }
    public int idcategoria { get; set; }

    ...

    [ForeignKey("idcategoria")] //it means idcategoria proerty above is the relevant property that links to PK property of Categoria
    public Categoria categoria { get; set; }
}

I guess just pick whichever you like more and be consistent. The examples in MSDN use dependent

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM