使用Entity Framework导航属性而不创建大量查询(避免N + 1)

Ric*_*ide 2 entity-framework navigation-properties entity-framework-4 select-n-plus-1

我一直在使用Entity Framework Profiler来测试我在MVC项目中的数据访问,并且由于N + 1问题我已经在几个页面上进行了比我需要的更多的数据库查询.

这是一个显示我的问题的简单示例:

var club = this.ActiveClub; // ActiveClub uses code similar to context.Clubs.First() 
var members = club.Members.ToList();
return View("MembersWithAddress", members);
Run Code Online (Sandbox Code Playgroud)

视图循环遍历成员,然后跟随每个成员的navigion属性以显示其地址.每个地址请求都会产生额外的数据库查询.

解决此问题的一种方法是使用Include来确保我需要的额外表格在前面查询.但是,我似乎只能在直接附加到上下文的Clubs的ObjectSet上执行此操作.在这种情况下,ActiveClub属性由许多控制器共享,我并不总是想要预先查询成员和地址表.

我希望能够使用类似的东西:

var members = club.Members.Include("Address").ToList();
Run Code Online (Sandbox Code Playgroud)

但是,成员是一个EntityCollection,并且没有包含Include方法.

有没有办法强制加载成员EntityCollection并要求EF也加载他们的地址?

或者,以这种方式在实体上使用EntityCollection导航属性,这只是一个非常糟糕的主意; 当你从上下文中得到它时,你应该知道你正在加载什么?

Lad*_*nka 5

如果您的实体继承自EntityObject尝试使用此:

var members = club.Members.CreateSourceQuery()
                          .Include("Address")
                          .ToList();
Run Code Online (Sandbox Code Playgroud)

如果您使用延迟加载代理的POCO尝试使用此:

var members = ((EntityCollection<Club>)club.Members).CreateSourceQuery()
                                                    .Include("Address")
                                                    .ToList();
Run Code Online (Sandbox Code Playgroud)

显然第二个版本不是很好,因为POCO用于删除对EF的依赖,但现在你需要将集合转换为EF类.另一个问题是查询将被执行两次.延迟加载将触发Members一次访问属性,然后在您调用时将执行第二次查询ToList.这可以通过在运行查询之前关闭延迟加载来解决.

当你说ActiveClub共享时我相信它意味着类似于派生控制器中使用的基本控制器中的属性.在这种情况下,您仍然可以在不同的控制器中使用不同的代码来填充属性.