The relation between Product and Customer is of type many-to-many (from a design point a view).
Using EF Core, we split this relation in two one-to-many relations with a third entity: ProductCustomer
public partial class ProductCustomer
{
public long ProductId { get; set; }
public long CustomerId { get; set; }
public virtual Customer Customer { get; set; }
public virtual Product Product { get; set; }
public virtual ICollection<UsageRecord> UsageRecord { get; set; }
}
UsageRecord is a list of Records containing the quantity of data used by a certain customer while he is using a product
public partial class UsageRecord
{
public long Id { get; set; }
public long ProductId { get; set; }
public long CustomerId { get; set; }
public decimal Quantity { get; set; }
public virtual ProductCustomer ProductCustomer { get; set; }
}
Now, if i try to read a specific UsageRecord, the ProductCustomer object is null (perfect, i am using an eager loading approach)
return _usageRecordEntity.Where(x => x.ProductId == productId).AsEnumerable();
But if i specifically ask to Include() the ProductCustomer entity, the entity framwork, not only includes all the recursive references but also includes the Product object and NOT the Customer!
return _usageRecordEntity.Where(x => x.ProductId == productId).Include(p => p.ProductCustomer).AsEnumerable();
First thing: I don't understand why it is including the whole chain of objects if i specifically ask just for the ProductCustomer one.
Second thing: Why the Product and NOT the Customer?!
I include for completeness the Context model:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>(entity =>
{
entity.Property(e => e.customerId)
.IsRequired()
.HasColumnName("CustomerId")
.HasMaxLength(255);
});
modelBuilder.Entity<Product>(entity =>
{
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(50);
});
modelBuilder.Entity<ProductCustomer>(entity =>
{
entity.HasKey(e => new { e.ProductId, e.CustomerId })
.HasName("PK__ProductCustomerComposite");
entity.HasOne(d => d.Customer)
.WithMany(p => p.ProductCustomer)
.HasForeignKey(d => d.CustomerId)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK__ProductCu__CustomerId");
entity.HasOne(d => d.Product)
.WithMany(p => p.ProductCustomer)
.HasForeignKey(d => d.ProductId)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK__ProductCu__ProductId");
});
modelBuilder.Entity<UsageRecord>(entity =>
{
entity.Property(e => e.Quantity)
.HasColumnType("decimal")
.HasDefaultValueSql("0");
entity.HasOne(d => d.ProductCustomer)
.WithMany(p => p.UsageRecord)
.HasForeignKey(d => new { d.ProductId, d.CustomerId })
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK_UsageRecordProductcustomer");
});
}
Basically the answer is provided by the following Tip in the Loading Related Data - Eager loading section of the EF Core documentation (highlight is mine):
Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With