如何避免EF生成的查询中出现N+1问题

3
我曾经自己生成所有助手表,例如用于多对多关系或POCO中的关系ID的,但是现在我希望EF来处理它们。不过,我现在认为这并不是一个好主意。
问题:当我尝试获取特定用户的所有时,它会为用户所在的每个用户组生成个查询。
这里,我通过简单地指出将成为而不是来克服了这个问题。现在我不能这样做,因为EF不会映射它,它必须是。
代码:
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
                };
            }
        }
    }
}

Database

DB DB2

2个回答

1

我曾经通过简单地声明用户组将是IQueryable而不是IEnumerable来解决这个问题。但现在我不能这样做,因为EF不会映射它,必须是ICollection。

但最终实现ICollection的类是EntityCollection<T>。这个集合有一个CreateSourceQuery()函数,你可以使用它:

var usergroupsQuery = ((EntityCollection<UserGroup>)this.Usergroups).CreateSourceQuery();
var fields = usergroupsQuery.SelectMany(x => x.UsergroupDynamicFields);
< p > < em > 更新 :如评论中所指出的那样,只有在更改跟踪可行且启用时(非密封类以及所有相关属性 < code > virtual ),< code > ICollection<T> 才会使用 < code > EntityCollection<T> 实现。您可以通过另一种方式创建查询:
var usergroupsQuery = db.Entry(this).Collection(u => u.Usergroups).Query();
var fields = usergroupsQuery.SelectMany(x => x.UsergroupDynamicFields);

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

哦,第一行代码给了我一个错误:无法将类型为'System.Collections.Generic.HashSet'1[Api.Models.Users.Usergroup]'的对象强制转换为类型'System.Data.Objects.DataClasses.EntityCollection'1[Api.Models.Users.Usergroup]'。 - Stan
@Steve,这不是EF创建集合时通常使用的方式。你的实体是否已附加到上下文中,或者你是以其他方式创建它的? - user743382
当然,这是附加在上下文中的。但它非常标准,例如 public DbSet<User> Users { get; set; } 等等。 - Stan
@Steve 根据这个答案EntityCollection<T>仅在更改跟踪可能时使用,而更改跟踪是否可能取决于您如何配置类和其他属性。我会在可以检查相关细节时更新我的答案。 - user743382
我不知道。我回到使用POCO来处理所有的事情,包括辅助表和关系ID。这个“EF会处理一切”的说法不值得。不过我会把你标记为答案。 - Stan

0

我尝试着用类似的东西

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);
                } 
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接