简体   繁体   English

用于嵌套导航属性的实体框架“包含”会产生比预期更多的数据

[英]Entity Framework `Include` for nested navigation properties results in more data than expected

private IQueryable<MyEntity> GetEntities(MyDataContext dc)
{
    return dc.MyEntities.Include(
        entity => entity.RelationalEntities.Select(
            relationalEntity => relationalEntity.ChildEntities
        )
    );
}

At a glance, this appears correct.乍一看,这似乎是正确的。

However, after taking a closer look at the data returned, we find that ChildEntities incorrectly contains a collection of all relations in the relational table, even though it should only contain ChildEntity entries that relate to the entity object referred to by the RelationalEntity table.然而,在仔细查看返回的数据后,我们发现ChildEntities错误地包含了关系表中所有关系的集合,尽管它应该只包含与RelationalEntity表引用的entity object 相关的ChildEntity条目。

To put it simply, we are getting more ChildEntity entries than expected from this query, and there does not appear to be a way to filter on them by using .Where() in these .Include() calls due to framework limitations.简而言之,我们从这个查询中获得了比预期更多的ChildEntity条目,并且由于框架限制,似乎没有办法通过在这些.Include()调用中使用.Where()来过滤它们。

Ideally, I would be able to extend the query as follows, but this code results in Entity Framework exceptions:理想情况下,我可以按如下方式扩展查询,但此代码会导致实体框架异常:

private IQueryable<MyEntity> GetEntities(RcmEntities dc)
{
    return dc.MyEntities.Include(
        entity => entity.RelationalEntity.Select(
            relationalEntity => relationalEntity.ChildEntities.Where(
                childEntity => childEntity.MyEntityId == entity.MyEntityId
                 && childEntity.ChildEntityId == relationalEntity.ChildEntityId
            )
        )
    );
}

The table relation looks something like this:表关系如下所示:

MyEntity.MyEntityId -> RelationalEntity.MyEntityId, RelationalEntity.ChildEntityId -> ChildEntity.ChildEntityId

How can I adjust this Entity Framework query to only return ChildEntity entries that relate to the referred MyEntity entry?如何调整此实体框架查询以仅返回与引用的MyEntity条目相关的ChildEntity条目?

Edit 1:编辑1:

By request, here is an exact copy of our entity classes, with all domain-specific information removed:根据要求,这里是我们实体类的精确副本,删除了所有特定于域的信息:

namespace TransportTypes
{
    using Newtonsoft.Json;

    // Entity
    [System.CodeDom.Compiler.GeneratedCode("EF.Reverse.POCO.Generator", "2.37.4.0")]
    public partial class Entity
    {
        public int EntityId { get; set; } // EntityId (Primary key)
        
        // Removed domain-specific fields...

        // Reverse navigation

        /// <summary>
        /// Child ChildEntitys (Many-to-Many) mapped by table [EntityEnabledChild]
        /// </summary>
        [JsonIgnore]
        public virtual System.Collections.Generic.ICollection<ChildEntity> ChildEntitys { get; set; } = new System.Collections.Generic.List<ChildEntity>(); // Many to many mapping
        /// <summary>
        /// Child ChildEntityContents where [ChildEntityContent].[EntityId] point to this entity (FK_ChildEntityContent_Entity)
        /// </summary>
        [JsonIgnore]
        public virtual System.Collections.Generic.ICollection<ChildEntityContent> ChildEntityContents { get; set; } = new System.Collections.Generic.List<ChildEntityContent>(); // ChildEntityContent.FK_ChildEntityContent_Entity
    }

    // Entity
    [System.CodeDom.Compiler.GeneratedCode("EF.Reverse.POCO.Generator", "2.37.4.0")]
    public partial class EntityConfiguration : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<Entity>
    {
        public EntityConfiguration()
            : this("dbo")
        {
        }

        public EntityConfiguration(string schema)
        {
            ToTable("Entity", schema);
            HasKey(x => x.EntityId);

            Property(x => x.EntityId).HasColumnName(@"EntityId").HasColumnType("int").IsRequired().HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
            HasMany(t => t.ChildEntitys).WithMany(t => t.Entitys).Map(m =>
            {
                m.ToTable("EntityEnabledChild", "dbo");
                m.MapLeftKey("EntityId");
                m.MapRightKey("ChildEntityId");
            });
            InitializePartial();
        }
        partial void InitializePartial();
    }

    // ChildEntity
    [System.CodeDom.Compiler.GeneratedCode("EF.Reverse.POCO.Generator", "2.37.4.0")]
    public partial class ChildEntity
    {
        public int ChildEntityId { get; set; } // ChildEntityId (Primary key)

        // Reverse navigation

        /// <summary>
        /// Child Entitys (Many-to-Many) mapped by table [EntityEnabledChild]
        /// </summary>
        [JsonIgnore]
        public virtual System.Collections.Generic.ICollection<Entity> Entitys { get; set; } = new System.Collections.Generic.List<Entity>(); // Many to many mapping
        /// <summary>
        /// Child ChildEntityContents where [ChildEntityContent].[ChildEntityId] point to this entity (FK_ChildEntityContent_ChildEntity)
        /// </summary>
        [JsonIgnore]
        public virtual System.Collections.Generic.ICollection<ChildEntityContent> ChildEntityContents { get; set; } = new System.Collections.Generic.List<ChildEntityContent>(); // ChildEntityContent.FK_ChildEntityContent_ChildEntity
    }

    // ChildEntity
    [System.CodeDom.Compiler.GeneratedCode("EF.Reverse.POCO.Generator", "2.37.4.0")]
    public partial class ChildEntityConfiguration : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<ChildEntity>
    {
        public ChildEntityConfiguration()
            : this("dbo")
        {
        }

        public ChildEntityConfiguration(string schema)
        {
            ToTable("ChildEntity", schema);
            HasKey(x => x.ChildEntityId);

            Property(x => x.ChildEntityId).HasColumnName(@"ChildEntityId").HasColumnType("int").IsRequired().HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
            InitializePartial();
        }
        partial void InitializePartial();
    }

    // ChildEntityContent
    [System.CodeDom.Compiler.GeneratedCode("EF.Reverse.POCO.Generator", "2.37.4.0")]
    public partial class ChildEntityContent
    {
        public int EntityId { get; set; } // EntityId (Primary key)
        public int ChildEntityId { get; set; } // ChildEntityId (Primary key)
        public string ContentKey { get; set; } // ContentKey (Primary key) (length: 100)
        public string ContentValue { get; set; } // ContentValue

        // Foreign keys

        /// <summary>
        /// Parent Entity pointed by [ChildEntityContent].([EntityId]) (FK_ChildEntityContent_Entity)
        /// </summary>
        [JsonIgnore]
        public virtual Entity Entity { get; set; } // FK_ChildEntityContent_Entity

        /// <summary>
        /// Parent ChildEntity pointed by [ChildEntityContent].([ChildEntityId]) (FK_ChildEntityContent_ChildEntity)
        /// </summary>
        [JsonIgnore]
        public virtual ChildEntity ChildEntity { get; set; } // FK_ChildEntityContent_ChildEntity
    }

    // ChildEntityContent
    [System.CodeDom.Compiler.GeneratedCode("EF.Reverse.POCO.Generator", "2.37.4.0")]
    public partial class ChildEntityContentConfiguration : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<ChildEntityContent>
    {
        public ChildEntityContentConfiguration()
            : this("dbo")
        {
        }

        public ChildEntityContentConfiguration(string schema)
        {
            ToTable("ChildEntityContent", schema);
            HasKey(x => new { x.EntityId, x.ChildEntityId, x.ContentKey });

            Property(x => x.EntityId).HasColumnName(@"EntityId").HasColumnType("int").IsRequired().HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
            Property(x => x.ChildEntityId).HasColumnName(@"ChildEntityId").HasColumnType("int").IsRequired().HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
            Property(x => x.ContentKey).HasColumnName(@"ContentKey").HasColumnType("nvarchar").IsRequired().HasMaxLength(100).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
            Property(x => x.ContentValue).HasColumnName(@"ContentValue").HasColumnType("nvarchar(max)").IsOptional();

            // Foreign keys
            HasRequired(a => a.Entity).WithMany(b => b.ChildEntityContents).HasForeignKey(c => c.EntityId).WillCascadeOnDelete(false); // FK_ChildEntityContent_Entity
            HasRequired(a => a.ChildEntity).WithMany(b => b.ChildEntityContents).HasForeignKey(c => c.ChildEntityId).WillCascadeOnDelete(false); // FK_ChildEntityContent_ChildEntity
            InitializePartial();
        }
        partial void InitializePartial();
    }
}

If this is EF 6 then it should look like:如果这是 EF 6,那么它应该如下所示:

return dc.MyEntities
    .Include(entity => entity.RelationalEntities)
    .Include(entity => entity.RelationalEntities
        .Select(rentity => rentity.ChildEntities))
    .AsQueryable();

Your second example would be involving conditional, or filtered includes which are only supported in EF Core, in which case I believe it should be:您的第二个示例将涉及仅在 EF Core 中支持的条件或过滤包含,在这种情况下,我认为应该是:

return dc.MyEntities
    .Include(entity => entity.RelationalEntities)
       .ThenInclude(rentity => rentity.ChildEntities)
    .AsQueryable();

Beyond that it would depend on your specific entity definitions to ensure that your relationships between Entity, RelationalEntity, and ChildEntity are configured as proper 1-to-many or many-to-many relationships and not doing something unconventional resulting in them being too loosely associated resulting in unexpected query results.除此之外,这将取决于您的特定实体定义,以确保您将 Entity、RelationalEntity 和 ChildEntity 之间的关系配置为正确的一对多或多对多关系,并且不会做一些非常规的事情,从而导致它们之间的关联过于松散导致意外的查询结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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