【问题标题】:How to avoid N+1 in EF generated queries如何在 EF 生成的查询中避免 N+1
【发布时间】:2013-07-25 12:26:44
【问题描述】:

我曾经为 POCO 中的多对多关系或关系 ID 生成所有帮助表,如 UsergroupUsers,但现在我希望 EF 处理它们。现在我认为这毕竟不是一个好主意。

问题

当我尝试获取特定用户的所有 UsergroupDynamicField 时,它会为每个用户组用户所在的每个用户组生成 N+1 查询。

Here 我通过简单地声明Usergroups 将是IQUeriable 而不是IEnumerable 克服了这个问题。现在我不能这样做,因为 EF 不会映射它,它必须是 ICollection

代码

public class User
{
    ...
    public virtual ICollection<Usergroup> Usergroups { get; set; }
    public IEnumerable<UserField> Fields
    {
        get
        {
            var fields = this.Usergroups.SelectMany(x => x.UsergroupDynamicFields); // N + 1 for every Usergroup

            foreach (var field in fields)
            {
                yield return new UserField
                {
                    Name = field.Name
                };
            }
        }
    }
}

数据库

【问题讨论】:

    标签: c# entity-framework entity-framework-5


    【解决方案1】:

    在这里,我通过简单地说明用户组将是 IQUeriable 而不是 IEnumerable 来克服这个问题。现在我不能这样做,因为 EF 不会映射它,它必须是 ICollection。

    但最终实现ICollection 的类是EntityCollection&lt;T&gt;。这个集合有一个CreateSourceQuery() function,你可以使用:

    var usergroupsQuery = ((EntityCollection<UserGroup>)this.Usergroups).CreateSourceQuery();
    var fields = usergroupsQuery.SelectMany(x => x.UsergroupDynamicFields);
    

    更新:正如 cmets 中所指出的,ICollection&lt;T&gt; 将仅在可以并启用更改跟踪时使用 EntityCollection&lt;T&gt; 实现(非密封类和所有相关属性 virtual )。您可以通过其他方式创建查询:

    var usergroupsQuery = db.Entry(this).Collection(u => u.Usergroups).Query();
    var fields = usergroupsQuery.SelectMany(x => x.UsergroupDynamicFields);
    

    请注意,这要求您以某种方式访问​​db

    【讨论】:

    • 嗯,第一行给我Unable to cast object of type 'System.Collections.Generic.HashSet'1[Api.Models.Users.Usergroup]' to type 'System.Data.Objects.DataClasses.EntityCollection'1[Api.Models.Users.Usergroup]'.
    • @Steve 这不是 EF 在创建集合时通常使用的。您的实体是附加到上下文中的,还是以其他方式创建的?
    • 是的,当然附加到上下文中。但这是非常标准的上下文public DbSet&lt;User&gt; Users { get; set; } 等。
    • @Steve 根据this answerEntityCollection&lt;T&gt; 仅在可以更改跟踪时使用,是否可以进行更改跟踪取决于您如何配置类和其他属性。当我可以自己检查相关详细信息时,我会更新我的答案。
    • 我不知道。我恢复使用 POCO 处理所有内容、帮助表和关系 ID。这个“EF 会照顾好一切” 不值得。我会把你标记为答案。
    【解决方案2】:

    我尝试使用类似的东西

    var res = c.Users.Include("Groups.DynFields").First().Groups.SelectMany(x => x.DynFields).ToList();
    

    而且似乎没问题。我使用EF5。

    当然...这不是 User 类中的方法。它需要能够在 DbSet 对象上调用 Include 方法。

    我希望这会有所帮助。

    完整解决方案

     public class User {
            public Int32 Id { get; set; }
            public String Name { get; set; }
    
            public virtual ICollection<UserGroup> Groups { get; set; }
        }
    
        public class UserGroup {
            public Int32 Id { get; set; }
            public String Name { get; set; }
    
            public virtual ICollection<User> Users { get; set; }
    
            public virtual ICollection<UserGroupDynamicField> DynFields { get; set; }
        }
    
        public class UserGroupDynamicField {
            public Int32 Id { get; set; }
            public String Name { get; set; }
            public virtual UserGroup Group { get; set; }
        }
    
    
        public class UserGroupDynFEFCFConfiguration : EntityTypeConfiguration<UserGroupDynamicField > {
            public UserGroupDynFEFCFConfiguration()
                : base() {
                    HasRequired(x => x.Group);
            }
        }
    
        public class UserGroupEFCFConfiguration : EntityTypeConfiguration<UserGroup> {
            public UserGroupEFCFConfiguration()
                : base() {
                    HasMany(x => x.Users).WithMany(y => y.Groups);
            }
        }       
    
        public class TestEFContext : DbContext {
            public IDbSet<User> Users { get; set; }
            public IDbSet<UserGroup> Groups { get; set; }
    
            public TestEFContext(String cs)
                : base(cs) {
                Database.SetInitializer<TestEFContext>(new DropCreateDatabaseAlways<TestEFContext>());
            }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder) {
                base.OnModelCreating(modelBuilder);
    
                modelBuilder.Configurations.Add(new UserGroupDynFEFCFConfiguration());
                modelBuilder.Configurations.Add(new UserGroupEFCFConfiguration());
            }
        }
    
        class Program {
            static void Main(String[] args) {
                String cs = @"Data Source=ALIASTVALK;Initial Catalog=TestEF;Integrated Security=True; MultipleActiveResultSets=True";
    
                using (TestEFContext c = new TestEFContext(cs)) {
                    UserGroup g1 = new UserGroup {
                        Name = "G1",
                        DynFields = new List<UserGroupDynamicField> { 
                            new UserGroupDynamicField { Name = "DF11"},
                            new UserGroupDynamicField { Name = "DF12"}
                        }
                    };
                    c.Groups.Add(g1);
    
                    UserGroup g2 = new UserGroup {
                        Name = "G2",
                        DynFields = new List<UserGroupDynamicField> { 
                            new UserGroupDynamicField { Name = "DF21"},
                            new UserGroupDynamicField { Name = "DF22"}
                        }
                    };
                    c.Groups.Add(g2);
    
                    c.Users.Add(new User {
                        Name = "U1",
                        Groups = new List<UserGroup> { g1, g2 }
                    });
    
                    c.SaveChanges();
                }      
    
                using (TestEFContext c = new TestEFContext(cs)) {
                    var res = c.Users.Include("Groups.DynFields").First().Groups.SelectMany(x => x.DynFields).ToList();
                    foreach (var v in res) {
                        Console.WriteLine(v.Name);
                    } 
                }
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2018-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多